基本上,我想要的是 multi_types std::initializer_list
template<typename ...As>
struct Foo {
Foo(As...){}
};
template<typename ...As>
void f(Foo<As...>) {}
int main() {
f(Foo{5, 3.f}); // 1) compile
f({}); // 2) compile
f({5, 3}); // 3) error
f({5, 3.8}); // 4) error
return 0;
}
我确实理解第一个示例编译的原因。但是,我不明白为什么第二个编译但其他的不编译。对我来说,如果第三个和第四个不编译,第二个也不应该编译。有没有办法使第三次和第四次编译?
最佳答案
However, I do not understand why the second compiles but the others don't [compile].
示例 3 和 4 无法编译,因为推导的模板参数与您期望的不同。
当您编写对函数模板的调用 ( f({5, 3})
) 时 template<typename ...As> void f(Foo<As...>)
,编译器需要推断出任何缺失的模板参数(...As
)。这些缺失的模板参数首先通过比较函数参数 ({5, 3}
) 和函数参数 (Foo<As...>
) 推导出来。如果函数参数是初始化列表 ( {5, 3}
) [1],跳过函数参数的模板参数推导 ( Foo<As...>
):
N4659 [temp.deduct.call] 17.8.2.1(1):[...] an initializer list argument causes the parameter to be considered a non-deduced context (17.8.2.5).
N4659 [temp.deduct.type] 17.8.2.5(5.6):[Non-deduced contexts include a] function parameter for which the associated argument is an initializer list (11.6.4) [...]
因为尾随的模板参数包 ( As...
) 没有被任何函数参数推导出来,它被推导出为空:
N4659 [temp.arg.explicit] 17.8.1(3):A trailing template parameter pack (17.5.3) not otherwise deduced will be deduced to an empty sequence of template arguments.
对于 f({5, 3})
,编译器推断模板参数是 <>
(空),因此被调用的特化是void f(Foo<>)
强>。重要提示:无论初始化列表中有什么,都会选择此特化。这解释了编译器给出的诊断:
Clang:
error: no matching function for call to 'f'
note: candidate function [with As = <>] not viable: cannot convert initializer list argument to 'Foo<>'GCC:
error: could not convert '{5, 3}' from '' to 'Foo<>'MSVC:
error C2664: 'void f<>(Foo<>)': cannot convert argument 1 from 'initializer list' to 'Foo<>'
note: No constructor could take the source type, or constructor overload resolution was ambiguous
在函数调用中,函数参数使用函数参数中的复制初始化 进行初始化。以下语句使用与您的示例初始化 f
的参数相同的规则初始化变量:
Foo<> x = {}; // Example 2
Foo<> x = {5, 3}; // Example 3
Foo<> x = {5, 3.8}; // Example 4
[1] 简而言之,如果函数参数既不是 std::initializer_list<T>
也不T[n]
关于c++ - CTAD、initializer_list、非显式构造函数和函数调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54824308/