假设我们有以下代码:
#include <iostream>
#include <string>
struct A
{
A() {}
A(const A&) { std::cout << "Copy" << std::endl; }
A(A&&) { std::cout << "Move" << std::endl; }
std::string s;
};
struct B
{
A a;
};
int main()
{
B{A()};
}
在这里,我相信 struct A
不是一个聚合,因为它既有重要的构造函数,也有一个 std::string
成员,我认为这不是一个集合。这可能意味着 B
也不是聚合。
但我可以聚合初始化 B。此外,这可以在不调用复制或移动构造函数的情况下完成(例如 ideone)。
这种行为似乎是一种有用的优化,特别是对于将没有廉价移动的大型堆栈类型组合在一起。
我的问题是:这种聚合初始化在 C++0x 下什么时候有效?
编辑+跟进问题:
下面的 DeadMG 回答如下:
这根本不是聚合初始化,而是统一初始化,在这种情况下基本上意味着调用构造函数,并且没有复制或移动可能是由 RVO 和 NRVO 完成的。
请注意,当我将 B
更改为以下内容时:
struct B
{
A a;
B(const A& a_) : a(a_) {}
B(A&& a_) : a(std::move(a_)) {}
};
执行移动。
因此,如果这只是统一初始化并且只是调用构造函数而不执行任何特殊操作,那么我如何编写一个允许省略移动的构造函数?
或者,当有效时,GCC 是否只是不消除此处的移动,如果是这样,是否有编译器和优化设置可以消除移动?
Lets say we have the following code:
#include <iostream>
#include <string>
struct A
{
A() {}
A(const A&) { std::cout << "Copy" << std::endl; }
A(A&&) { std::cout << "Move" << std::endl; }
std::string s;
};
struct B
{
A a;
};
int main()
{
B{A()};
}
Here, I believe struct A
is not an aggregate, as it has both non-trivial constructors and also a std::string
member which I presume is not an aggregate. This presumably means that B
is also not an aggregate.
Yet I can aggregate initialize B. In addition this can be done without either the copy nor move constructor being called (e.g. C++0x GCC 4.5.1 on ideone).
This behavior seems like a useful optimization, particularly for composing together large stack types that don't have cheap moves.
My question is: When is this sort of aggregate initialization valid under C++0x?
Edit + follow up question:
DeadMG below answered with the following:
That's not aggregate initialization at all, it's uniform initialization, which basically in this case means calling the constructor, and the no copy or move is probably done by RVO and NRVO.
Note that when I change B
to the following:
struct B
{
A a;
B(const A& a_) : a(a_) {}
B(A&& a_) : a(std::move(a_)) {}
};
A move is performed.
So if this is just uniform initialization and just calling the constructor and doing nothing special, then how do I write a constructor that allows the move to be elided?
Or is GCC just not eliding the move here when it is valid to do so, and if so, is there a compiler and optimization setting that will elide the move?
发布评论
评论(2)
根据新标准第 8.5.1 条(聚合),足够简单的类型(例如,没有用户定义的构造函数)有资格作为聚合。对于这样的聚合
Foo
,编写Foo x{a, b, ... };
将从列表项构造成员。简单示例:
对象
x
是通过就地执行的所有相关构造函数来构造的。地图、字符串或 MyClass 不会被复制或移动。请注意,底部的两个变体执行相同的操作。如果您愿意,您甚至可以将 MyClass 的复制和移动构造函数设为私有。According to the new standard, clause 8.5.1 (Aggretates), a sufficiently simple type (e.g. no user-defined constructors) qualifies as an aggregate. For such an aggregate
Foo
, writingFoo x{a, b, ... };
will construct the members from the list items.Simple example:
The object
x
is constructed with all the relevant constructors executed in place. No maps or strings or MyClasses ever get copied or moved around. Note that both variants at at the bottom do the same thing. You can even make MyClass's copy and move constructors private if you like.这根本不是聚合初始化,而是统一初始化,在这种情况下基本上意味着调用构造函数,并且没有复制或移动可能是由 RVO 和 NRVO 完成的。
That's not aggregate initialization at all, it's uniform initialization, which basically in this case means calling the constructor, and the no copy or move is probably done by RVO and NRVO.