Yor*_*k B 6 c++ templates c++11
我试图理解为什么除非添加括号,否则以下代码段的替换会失败:
template<typename T>
struct A {};
template<typename T>
struct B {
B(A<T>);
};
template<typename T>
void example(A<T>, B<T>);
struct C {};
struct D {
D(C);
};
void example2(C, D);
int main(int argc, char *argv[]) {
example(A<int>{}, A<int>{}); // error
example(A<int>{}, {A<int>{}}); // ok
example2(C{}, C{}); // ok
example2(C{}, {C{}}); // ok
return 0;
}
Run Code Online (Sandbox Code Playgroud)
请参阅此示例:https : //godbolt.org/z/XPqHww
因为example2我能够将 隐式传递C{}给 的构造函数而D没有任何错误。因为example我不允许隐式传递A<int>{}直到我添加括号。
什么定义了这种行为?
example是一个函数模板,函数参数依赖于模板参数。因此,由于您没有在 call 中明确指定任何模板参数example(A<int>{}, A<int>{});,当您调用此函数时,将执行模板参数推导以确定T调用应该是什么类型。
除了少数例外,模板参数推导要求T可以找到a ,使得函数调用中的参数类型与函数参数中的类型完全匹配。
您的调用的问题是A<int>显然不B<T>完全匹配 任何T(并且也不适用任何例外),因此调用将失败。
这种行为是必要的,否则编译器将需要测试所有可能的类型T以检查是否可以调用该函数。这在计算上是不可行或不可能的。
在调用中example2(C{}, C{});不涉及模板,因此不执行模板参数推导。因为编译器不再需要找出参数中的目标类型,所以考虑从已知参数类型到已知参数类型的隐式转换变得可行。一种这样的隐式转换是通过非显式构造函数构造Dfrom 。因此,该调用通过该转换成功。CD(C);
example2(C{}, {C{}}); 效果相同。
那么问题是为什么example(A<int>{}, {A<int>{}});有效。这是因为可以在例如C++17 标准(草案 N4659)的[temp.deduct.type]/5.6中找到特定规则。它表示一个函数参数/参数对,其参数是一个初始化列表(即{A<int>{}}),并且该参数不是一个特化std::initializer_list或一个数组类型(这里既不是),函数参数是一个非推导的上下文。
非推导上下文意味着在模板参数推导期间不会使用函数参数/参数对来确定T. 这意味着它的类型不需要完全匹配。相反,如果模板参数推导成功,则结果T将简单地替换到非推导上下文中,并且从那里将像以前一样考虑隐式转换。由于非显式构造B<T>函数,可以从{A<int>}ifT = int构造B(A<T>);。
现在的问题是模板参数推导是否会成功并推导T = int. 只有T可以从另一个函数参数推导出a才能成功。确实还有第一个参数,它的类型完全匹配:A<int>/A<T>匹配T = int并且因为这个函数参数不使用初始化列表,它是一个上下文,T将从中推导出来。
因此,确实example(A<int>{}, {A<int>{}});从第一个参数中扣除将产生T = int并替换到第二个参数B<T>中使初始化/转换B<T>{A<int>{}}成功,因此调用是可行的。
如果您像 in 那样为两个参数使用初始值设定项列表example({A<int>{}}, {A<int>{}});,则两个参数/参数对都成为非推导的上下文,并且不会有任何可推导T的内容,因此调用将因推导失败而失败T。
您可以通过T显式指定使所有调用工作,这样模板参数推导就变得不必要了,例如:
example<int>(A<int>{}, A<int>{});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
65 次 |
| 最近记录: |