从 CPP reference 中了解到,关于列表初始化:
输出为
否则,T 的构造函数将被两个阶段考虑:
检查所有只接受 std::initializer_list 作为唯一参数或者作为其余参数有默认值的首个参数的构造函数,并通过重载决策来与 std::initializer_list 类型的单一参数进行匹配。
如果上一个阶段没有找到合适的构造函数,那么 T 的所有构造函数都参与重载决策。这里的重载决策是对列表初始化元素的构造函数进行调用,仅允许非缩窄转换。 如果在复制列表初始化期间找到显式构造函数,则编译失败(请注意,在简单的复制初始化中,不考虑显式构造函数)。
因此,首先考虑使用 initializer_list
的构造函数。如果失败,则将列表中的每个元素视为构造函数的参数。 然而
#include <iostream>
using namespace std;
struct A{
template <typename... Args> A(Args... li) { cout << sizeof...(Args) << endl;}
};
int main(){
A a = {2,3,4};
}
输出为
3
,这表明Args...
被展开为int,int,int
。为什么不直接使用单数形式的initializer_list<int>
呢?详见列表初始化中的细节,它应该是第一个尝试的构造函数类型。
initializer_list<int>
参数将导致在A(Args...)
中成功的模板参数推断。因此,我CPP引用的第一行(转换为initialiser_list
参数)适用于查找候选非模板构造函数,而您的引用适用于使用模板参数推断查找候选模板函数。如果模板或非模板函数都没有提供合适的候选项,则将每个列表元素视为单独的参数。 - AntiElephantC
的构造函数模板template<class T> C(T)
的参数传递不会创建一个初始化器列表构造函数,因为初始化器列表参数会导致相应的参数是一个无法推导的上下文(14.8.2.1)。“这就是我查阅了**[temp.deduct.call]**的原因。现在,将你程序中的main
改为std::initializer_list<int> l{2,3,4}; A a = l;
会编译通过。我不确定为什么。 - Igor Tandetnikstd::initializer_list
实例的情况。这种区别很重要,两者不能互换。 - Igor Tandetnik