列出聚合的初始化:它什么时候可以调用复制构造函数?
list initialization of aggregates: when can it invoke copy constructor?
考虑以下代码:
struct A {
int x;
};
int main() {
A a;
A b{a};
}
此程序是否符合 C++11 标准?在我的 N3797 副本中它说
8.5.4 List initialization [dcl.init.list]
3: List-initialization of an object or reference of type T
is defined as follows:
- If T
is an aggregate, aggregate initialization is performed (8.5.1).
- Otherwise, if T
is a specialization of std::initializer_list<E>
, ...
- Otherwise, if T
is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen using overload resolution. If a narrowing conversion is required to convert any of the types, the program is ill-formed.
- Otherwise, if the initializer list has a single element of type E
and either T
is not a reference type or it is reference-related to E
, the object or reference is initialized from that element; if a narrowing conversion is required to convert the element to T
, the program is ill-formed.
- Otherwise, if T
is a reference type, a pr-value temporary of the type reference by T
is copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary.
- Otherwise, if the initializer list has no elements, the object is value-initialized.
- Otherwise, the program is ill-formed.
示例的要点是,类型是聚合,但列表初始化应该调用复制构造函数。在 gcc 4.8
和 gcc 4.9
上,在 C++11 标准下,它失败了:
main.cpp: In function ‘int main()’:
main.cpp:7:8: error: cannot convert ‘A’ to ‘int’ in initialization
A b{a};
^
并说 A is not convertible to int
或类似的,因为聚合初始化失败。在 gcc 5.4
上,它在 C++11 标准下运行良好。
在 clang
上,您会遇到与 clang-3.5
、3.6
类似的错误,并且它会在 clang-3.7
上开始工作。
我知道它在 C++14 标准中是良构的,并且在缺陷报告中提到了它 here。
但是,我不明白为什么这被认为是标准中的缺陷。
标准写的时候,
"If X
, foo-initialization is performed. Otherwise, if Y
, bar-initialization is performed, .... Otherwise, the program is ill-formed.",
这是否意味着如果X
成立,但无法执行foo-initialization,那么我们应该检查Y
是否成立,然后尝试bar-initialization?
这将使示例工作,因为当聚合初始化失败时,我们不匹配 std::initializer_list
,我们匹配的下一个条件是“T
是 class 类型",然后我们考虑构造函数。
请注意,这 确实如此 似乎是它在这个修改后的示例中的工作方式
struct A {
int x;
};
int main() {
A a;
const A & ref;
A b{ref};
}
在 C++11 和 C++14 标准中,所有相同的编译器都以与前面示例相同的方式处理此问题。但似乎 CWG 缺陷记录中修改后的措辞不适用于此案例。上面写着:
If T
is a class type and the initializer list has a single element of type cv T
or a class type derived from T
, the object is initialized from that element.
http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1467
但在第二个代码示例中,初始化列表在技术上包含 const T &
。所以我不知道它是如何工作的,除非在聚合初始化失败之后,我们应该尝试构造函数。
我错了吗?聚合初始化失败后是否不应该尝试构造函数?
这是一个相关的例子:
#include <iostream>
struct B {
int x;
operator int() const { return 2; }
};
int main() {
B b{1};
B c{b};
std::cout << c.x << std::endl;
}
在clang-3.6
、gcc-4.8
、gcc-4.9
中打印2
,在clang-3.7
、gcc-5.0
中打印1
.
假设我错了,并且在 C++11 标准中,聚合的列表初始化应该是聚合初始化而不是别的,直到引入缺陷报告中的新措辞,它是一个错误即使我 select -std=c++11
在较新的编译器上也会发生这种情况?
When the standard writes,
"If X
, foo-initialization is performed. Otherwise, if Y
,
bar-initialization is performed, ...
doesn't this mean that if X
holds, but foo-initialization cannot be
performed, then we should check if Y
holds, and then attempt
bar-initialization?
没有。如果 X
成立,我们将执行 foo 初始化。如果失败,则程序格式错误。
When the standard writes,
"If X, foo-initialization is performed. Otherwise, if Y, bar-initialization is performed, .... Otherwise, the program is ill-formed.",
doesn't this mean that if X holds, but foo-initialization cannot be performed, then we should check if Y holds, and then attempt bar-initialization?
不,不是。把它想象成实际的代码:
T *p = ...;
if(p)
{
p->Something();
}
else
{ ... }
p
不为 NULL。这也不意味着它是一个有效的指针。如果 p
指向一个被销毁的对象,p->Something()
失败不会导致您跳到 else
。您有机会在这种情况下保护通话。
所以你得到了未定义的行为。
这里也是一样。如果 X,则执行 A。这并不意味着如果 A 失败会发生什么;它告诉你去做。如果做不到...你就完蛋了。
考虑以下代码:
struct A {
int x;
};
int main() {
A a;
A b{a};
}
此程序是否符合 C++11 标准?在我的 N3797 副本中它说
8.5.4 List initialization
[dcl.init.list]
3: List-initialization of an object or reference of type
T
is defined as follows:
- IfT
is an aggregate, aggregate initialization is performed (8.5.1).
- Otherwise, ifT
is a specialization ofstd::initializer_list<E>
, ...
- Otherwise, ifT
is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen using overload resolution. If a narrowing conversion is required to convert any of the types, the program is ill-formed.
- Otherwise, if the initializer list has a single element of typeE
and eitherT
is not a reference type or it is reference-related toE
, the object or reference is initialized from that element; if a narrowing conversion is required to convert the element toT
, the program is ill-formed.
- Otherwise, ifT
is a reference type, a pr-value temporary of the type reference byT
is copy-list-initialized or direct-list-initialized, depending on the kind of initialization for the reference, and the reference is bound to that temporary.
- Otherwise, if the initializer list has no elements, the object is value-initialized.
- Otherwise, the program is ill-formed.
示例的要点是,类型是聚合,但列表初始化应该调用复制构造函数。在 gcc 4.8
和 gcc 4.9
上,在 C++11 标准下,它失败了:
main.cpp: In function ‘int main()’:
main.cpp:7:8: error: cannot convert ‘A’ to ‘int’ in initialization
A b{a};
^
并说 A is not convertible to int
或类似的,因为聚合初始化失败。在 gcc 5.4
上,它在 C++11 标准下运行良好。
在 clang
上,您会遇到与 clang-3.5
、3.6
类似的错误,并且它会在 clang-3.7
上开始工作。
我知道它在 C++14 标准中是良构的,并且在缺陷报告中提到了它 here。
但是,我不明白为什么这被认为是标准中的缺陷。
标准写的时候,
"If X
, foo-initialization is performed. Otherwise, if Y
, bar-initialization is performed, .... Otherwise, the program is ill-formed.",
这是否意味着如果X
成立,但无法执行foo-initialization,那么我们应该检查Y
是否成立,然后尝试bar-initialization?
这将使示例工作,因为当聚合初始化失败时,我们不匹配 std::initializer_list
,我们匹配的下一个条件是“T
是 class 类型",然后我们考虑构造函数。
请注意,这 确实如此 似乎是它在这个修改后的示例中的工作方式
struct A {
int x;
};
int main() {
A a;
const A & ref;
A b{ref};
}
在 C++11 和 C++14 标准中,所有相同的编译器都以与前面示例相同的方式处理此问题。但似乎 CWG 缺陷记录中修改后的措辞不适用于此案例。上面写着:
If
T
is a class type and the initializer list has a single element of typecv T
or a class type derived fromT
, the object is initialized from that element.
http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1467
但在第二个代码示例中,初始化列表在技术上包含 const T &
。所以我不知道它是如何工作的,除非在聚合初始化失败之后,我们应该尝试构造函数。
我错了吗?聚合初始化失败后是否不应该尝试构造函数?
这是一个相关的例子:
#include <iostream>
struct B {
int x;
operator int() const { return 2; }
};
int main() {
B b{1};
B c{b};
std::cout << c.x << std::endl;
}
在clang-3.6
、gcc-4.8
、gcc-4.9
中打印2
,在clang-3.7
、gcc-5.0
中打印1
.
假设我错了,并且在 C++11 标准中,聚合的列表初始化应该是聚合初始化而不是别的,直到引入缺陷报告中的新措辞,它是一个错误即使我 select -std=c++11
在较新的编译器上也会发生这种情况?
When the standard writes,
"If
X
, foo-initialization is performed. Otherwise, ifY
, bar-initialization is performed, ...doesn't this mean that if
X
holds, but foo-initialization cannot be performed, then we should check ifY
holds, and then attempt bar-initialization?
没有。如果 X
成立,我们将执行 foo 初始化。如果失败,则程序格式错误。
When the standard writes,
"If X, foo-initialization is performed. Otherwise, if Y, bar-initialization is performed, .... Otherwise, the program is ill-formed.",
doesn't this mean that if X holds, but foo-initialization cannot be performed, then we should check if Y holds, and then attempt bar-initialization?
不,不是。把它想象成实际的代码:
T *p = ...;
if(p)
{
p->Something();
}
else
{ ... }
p
不为 NULL。这也不意味着它是一个有效的指针。如果 p
指向一个被销毁的对象,p->Something()
失败不会导致您跳到 else
。您有机会在这种情况下保护通话。
所以你得到了未定义的行为。
这里也是一样。如果 X,则执行 A。这并不意味着如果 A 失败会发生什么;它告诉你去做。如果做不到...你就完蛋了。