来自相同类型对象的列表初始化优先级
List-initialization priority from object of same type
#include <iostream>
#include <initializer_list>
using namespace std;
struct CL
{
CL(){}
CL (std::initializer_list<CL>){cout<<1;}
CL (const CL&){cout<<2;}
};
int main()
{
CL cl1;
CL cl2 {cl1}; //prints 21
}
这是带有复制构造函数和初始化列表构造函数的 CL 结构。我认为这里只能调用复制构造函数,因为根据 C++ 14 标准,8.5.4/3
List-initialization of an object or reference of type T is defined as
follows:
— If T is a class type and the initializer list has a
single element of type cv U, where U is T or a class derived from T,
the object is initialized from that element (by copy-initialization
for copy-list-initialization, or by direct-initialization for
direct-list-initialization).
— Otherwise, ...
换句话说,cl2的初始化必须从cl1元素执行,而不是从initializer-list {cl1}。 Clang 和 gcc 都打印“21”,只有 Visual Studio 打印“2”,我认为它是正确的。
有两个候选构造函数用于接受类型为 CL:
的参数 cl1
- 具有
std::initializer_list<CL>
的构造函数(通过,因为没有从 CL 到 std::initializer_list<CL>
的此类转换)
- 用 const CL& 复制构造函数(精确匹配只限定转换 non-const->const)
谁是对的?谁的行为是正确的?
tl;dr: 已发布的 C++14 文本指定输出 21
。但是,此代码的行为已被 CWG Issue 1467 更改,它在 2014 年 11 月获得了缺陷状态。
缺陷报告被认为具有追溯力。 clang 3.7 和 VS2015 已应用此缺陷报告建议的解决方案,该缺陷报告出现在 N4296 的 C++17 草案中。
在此缺陷报告之前,该行为已包含在 N4140 [over.match.list]:
的这段文本中
When objects of non-aggregate class type T
are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
- Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
- If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.
If the initializer list has no elements and T has a default constructor, the first phase is omitted. In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [Note: This differs from other situations (13.3.1.3, 13.3.1.4), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution.
—end note ]
您的 class 不是聚合,因为它有一个 user-provided 构造函数。
上面的文字是 directed-to [dcl.init.list]/3 中的以下要点:
- Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7).
因此,在已发布的 C++14 中,initializer-list 构造函数实际上应该优先于 copy-constructor,如果它匹配的话。 C++11 有相同的文本。
在你的问题中你说 C++14 包含:
If T
is a class type and the initializer list has a single element of type [...]
此文本不在 C++14 中,但后来通过缺陷报告应用。在应用了缺陷报告的更新标准 (N4296) 中,这在 [dcl.init.list]/3 中的要点列表中显示为更高的要点;所以现在 copy-constructor 在这个过程中被更早地选择了,我们还没有达到上面的 [over.match.list] 步骤。
请注意,尽管该缺陷的标题为 List-initialization of aggregate from same-type object,但该解决方案实际上会影响两个聚合的初始化和non-aggregates.
#include <iostream>
#include <initializer_list>
using namespace std;
struct CL
{
CL(){}
CL (std::initializer_list<CL>){cout<<1;}
CL (const CL&){cout<<2;}
};
int main()
{
CL cl1;
CL cl2 {cl1}; //prints 21
}
这是带有复制构造函数和初始化列表构造函数的 CL 结构。我认为这里只能调用复制构造函数,因为根据 C++ 14 标准,8.5.4/3
List-initialization of an object or reference of type T is defined as follows:
— If T is a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).
— Otherwise, ...
换句话说,cl2的初始化必须从cl1元素执行,而不是从initializer-list {cl1}。 Clang 和 gcc 都打印“21”,只有 Visual Studio 打印“2”,我认为它是正确的。
有两个候选构造函数用于接受类型为 CL:
- 具有
std::initializer_list<CL>
的构造函数(通过,因为没有从 CL 到std::initializer_list<CL>
的此类转换) - 用 const CL& 复制构造函数(精确匹配只限定转换 non-const->const)
谁是对的?谁的行为是正确的?
tl;dr: 已发布的 C++14 文本指定输出 21
。但是,此代码的行为已被 CWG Issue 1467 更改,它在 2014 年 11 月获得了缺陷状态。
缺陷报告被认为具有追溯力。 clang 3.7 和 VS2015 已应用此缺陷报告建议的解决方案,该缺陷报告出现在 N4296 的 C++17 草案中。
在此缺陷报告之前,该行为已包含在 N4140 [over.match.list]:
的这段文本中When objects of non-aggregate class type
T
are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
- Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
- If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.
If the initializer list has no elements and T has a default constructor, the first phase is omitted. In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [Note: This differs from other situations (13.3.1.3, 13.3.1.4), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution. —end note ]
您的 class 不是聚合,因为它有一个 user-provided 构造函数。
上面的文字是 directed-to [dcl.init.list]/3 中的以下要点:
- Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7).
因此,在已发布的 C++14 中,initializer-list 构造函数实际上应该优先于 copy-constructor,如果它匹配的话。 C++11 有相同的文本。
在你的问题中你说 C++14 包含:
If
T
is a class type and the initializer list has a single element of type [...]
此文本不在 C++14 中,但后来通过缺陷报告应用。在应用了缺陷报告的更新标准 (N4296) 中,这在 [dcl.init.list]/3 中的要点列表中显示为更高的要点;所以现在 copy-constructor 在这个过程中被更早地选择了,我们还没有达到上面的 [over.match.list] 步骤。
请注意,尽管该缺陷的标题为 List-initialization of aggregate from same-type object,但该解决方案实际上会影响两个聚合的初始化和non-aggregates.