#include <initializer_list>
#include <utility>
template<class T>
struct test
{
test(const std::pair<T, T> &)
{}
};
template<class T>
test(std::initializer_list<T>) -> test<T>;
int main()
{
test t{{1, 2}};
}
我想了解为什么编译带有initializer_list
的技巧。看起来起初,{1,2}被视为initializer_list
,但随后它被重新解释为pair
的列表初始化。
在这里,逐步发生了什么?
答案 0 :(得分:2)
之所以进行编译,是因为这就是类模板推导指南的工作方式。
推论指南是假设类型的构造函数。它们确实不存在。它们的唯一目的是确定如何推断类模板参数。
进行推论后,实际的C ++代码将以特定的test
实例来接管。因此,编译器的行为就像您说过test t{{1, 2}};
一样,而不是test<int> t{{1, 2}};
。
test<int>
具有一个采用pair<int, int>
的构造函数,该构造函数可以匹配braced-init-list中的值,因此被调用。
做这种事情的一部分是为了使聚合参与类模板参数的推导。聚合没有用户提供的构造函数,因此,如果推导仅限于实际的构造函数,则聚合将无法进行。
因此,我们获得了std::array
的该类模板推导指南:
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
这允许std::array arr = {2, 4, 6, 7};
工作。它从指南中推导了模板参数和长度,但是由于指南不是构造函数,array
可以保持聚合。
答案 1 :(得分:1)
根据您的推论指南,我们得出的结论等同于:
test<int> t{{1, 2}};
由于列表初始化,第dcl.init.listp3.7节说:
否则,如果T是类类型,则考虑构造函数。的 列举适用的构造函数并选择最佳的构造函数 通过过载解析([over.match],[over.match.list])。如果一个 缩小转换(请参见下文)才能转换任何 参数,程序格式不正确。 [示例:
struct S { S(std::initializer_list<double>); // #1 S(std::initializer_list<int>); // #2 S(); // #3 // ... }; S s1 = { 1.0, 2.0, 3.0 }; // invoke #1 S s2 = { 1, 2, 3 }; // invoke #2 S s3 = { }; // invoke #3
-示例example] [示例:
struct Map { Map(std::initializer_list<std::pair<std::string,int>>); }; Map ship = {{"Sophie",14}, {"Surprise",28}};
-示例example] [示例:
struct S { // no initializer-list constructors S(int, double, double); // #1 S(); // #2 // ... }; S s1 = { 1, 2, 3.0 }; // OK: invoke #1 S s2 { 1.0, 2, 3 }; // error: narrowing S s3 { }; // OK: invoke #2
-示例example]
否则,我们会有一个non-deduced context