c++ - 类模板专业化推导是否应该推导引导参数初始化?
问题描述
作为这个问题的后续,我测试了 clang 和 gcc 的行为。看来这两个编译器对 c++ 标准有不同的解释。
在下面的示例中,如果需要根据演绎指南假设的构造函数参数复制不可复制的参数,则 GCC 拒绝编译。Clang 不执行此检查:
#include <cstddef>
struct not_copyable{
not_copyable()=default;
not_copyable(const not_copyable&)=delete;
};
struct movable{
movable()=default;
movable(movable&&);
};
template <typename T, size_t N>
struct A
{ template <typename ... Ts> A (Ts const & ...) {} };
template <typename T, size_t N>
struct B
{ template <typename ... Ts> B (const Ts & ...) {} };
template <typename T, typename ... Ts>
A(T const &, Ts const & ...) -> A<T, 1U + sizeof...(Ts)>;
template <typename T, typename ... Ts>
B(T, Ts ...) -> B<T, 1 + sizeof...(Ts)>;
int main()
{
not_copyable nc;
movable m;
auto a0 = A{nc}; // gcc & clang -> compile
auto a1 = A{m}; // gcc & clang -> compile
auto b0 = B{nc}; // clang ->compile; gcc -> error
auto b1 = B{m}; // clang ->compile; gcc -> error
}
认为正确的行为在 C++ 标准[over.match.class.deduct]/2的这一段中定义:
初始化和重载解析按照 [dcl.init] 和 [over.match.ctor]、[over.match.copy] 或 [over.match.list] 中的描述执行(根据执行的初始化类型)假设类类型的对象,其中选定的函数和函数模板被认为是该类类型的构造函数,以形成重载集,[...]
我强调“为了形成重载集”,因为我认为这是 clang 和 gcc 分歧的地方。Clang 似乎没有检查推导指南假设的构造函数是否是一个可行的函数,但 gcc 可以。哪个编译器是对的?
解决方案
Clang 似乎不会检查推导指南假设的构造函数是否是可行的函数,但 gcc 会。
实际上,演绎指南是一个可行的功能。一个函数是可行的只是意味着参数的数量匹配,约束得到满足,并且您可以为每个参数/参数对形成隐式转换序列。当我们检查 ICS 是否存在时,[over.best.ics]/2:
其他属性,例如生存期、存储类、对齐方式、参数的可访问性、参数是否为位域以及函数是否被删除,都将被忽略。
删除一个函数并不会使它不可行,这一点非常重要,因为重要的是它最终仍然可以成为最佳可行的候选者。这意味着not_copyable
' 的复制构造函数被删除的事实应该只在我们实际调用它时才生效。
例如,gcc 和 clang 都拒绝这个程序。#1
是一个可行的候选者,并且它是最好的候选者,尽管删除了复制构造函数:
struct NC {
NC() = default;
NC(NC const&) = delete;
NC& operator=(NC const&) = delete;
};
void foo(NC ); // #1
template <typename T> void foo(T const&); // #2
int main() {
NC nc;
foo(nc);
}
但是我们从来没有真正调用我们用于推导的合成函数和函数模板。我们只是在执行重载决议并选择最佳候选者——我们只是用来选择类类型,然后我们重新开始。我们在任何时候都不应该真正需要复制。
我认为这是一个 gcc 错误。归档86439 。
推荐阅读
- android - 创建具有动态颜色的自定义 xml 可绘制对象
- vue.js - 如何解决 vue-tables-2 中的嵌入排序问题?
- apache-kafka - 代理日志中有关混合偏移提交类型的警告
- cordova - Cordova 插件文档扫描仪
- c# - VLC 控制器不渲染视频
- java - Java - 通过向父 JPanel 添加更多 JPanel 来自动滚动条
- wso2 - WSO2 无效请求行:GET /
- go - Go 错误:恐慌:运行时错误:无效的内存地址或 nil 指针取消引用。更改存在于另一个结构中的结构内的映射,
- javascript - javascript window.find在特定div中区分大小写搜索
- angular7 - 在angular7中创建标签的困难