10

假设我们有以下代码:

#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()};
}

在这里,我相信 structA不是聚合,因为它既有重要的构造函数,也有一个std::string我认为不是聚合的成员。这大概意味着这B也不是一个聚合。

但是我可以聚合初始化 B。此外,这可以在不调用复制或移动构造函数的情况下完成(例如ideone上的 C++0x GCC 4.5.1 )。

这种行为似乎是一种有用的优化,特别是对于组合没有廉价移动的大型堆栈类型。

我的问题是:这种聚合初始化在 C++0x 下何时有效?

编辑+跟进问题:

下面的 DeadMG 回答如下:

那根本不是聚合初始化,而是统一初始化,在这种情况下,这基本上意味着调用构造函数,并且不复制或移动可能是由 RVO 和 NRVO 完成的。

请注意,当我更改B为以下内容时:

struct B
{
  A a;
  B(const A& a_) : a(a_) {}
  B(A&& a_) : a(std::move(a_)) {}
};

执行移动。

因此,如果这只是统一初始化并且只是调用构造函数并且没有做任何特别的事情,那么我如何编写一个允许忽略移动的构造函数?

或者 GCC 只是没有在有效的情况下忽略此处的移动,如果是这样,是否有编译器和优化设置可以忽略移动?

4

2 回答 2

6

根据新标准第 8.5.1 节(聚合),足够简单的类型(例如,没有用户定义的构造函数)有资格作为聚合。对于这样的聚合Foo,写作Foo x{a, b, ... };将从列表项中构造成员。

简单的例子:

struct A
{
  std::unordered_map<int, int> a;
  std::string b;
  std::array<int,4> c;
  MyClass d; // Only constructor is MyClass(int, int)
};

// Usage:
 A x{{{1,-1}, {12, -2}}, "meow", {1,2,3,4}, MyClass(4,4)};
// Alternative:
 A x{{{1,-1}, {12, -2}}, "meow", {1,2,3,4}, {4,4}};

该对象x是用所有相关的构造函数就地执行的。任何地图、字符串或 MyClass 都不会被复制或移动。请注意,底部的两个变体都做同样的事情。如果您愿意,您甚至可以将 MyClass 的复制和移动构造函数设为私有。

于 2011-06-08T02:44:05.620 回答
2

那根本不是聚合初始化,而是统一初始化,在这种情况下,这基本上意味着调用构造函数,并且不复制或移动可能是由 RVO 和 NRVO 完成的。

于 2011-06-08T02:00:13.300 回答