Когда агрегированная инициализация действительна в С++ 11?

Допустим, у нас есть следующий код:

#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. Кроме того, это можно сделать без вызова вызываемого экземпляра или перемещения конструктора (например, С++ 0x GCC 4.5.1 на ideone).

Такое поведение кажется полезной оптимизацией, особенно для компоновки больших типов стеков, которые не имеют дешевых ходов.

Мой вопрос: когда этот тип инициализации агрегата действителен в С++ 0x?

Изменить + следующий вопрос:

DeadMG ниже ответил следующим:

Это не сводная инициализация вообще, это равномерная инициализация, которая в основном в этом случае означает вызов конструктора, а no copy или move, вероятно, выполняются RVO и NRVO.

Обратите внимание, что когда я меняю B на следующее:

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

Выполняется перемещение.

Итак, если это просто равномерная инициализация и просто вызов конструктора и ничего особенного не делает, то как я могу написать конструктор, который позволяет двигаться в сторону?

Или GCC просто не ускоряет движение здесь, когда это действительно так, и если да, существует ли параметр компилятора и оптимизации, который будет преодолевать ход?

Ответ 1

В соответствии с новым стандартом, пунктом 8.5.1 (Aggretates), достаточно простой тип (например, не определенные пользователем конструкторы) квалифицируется как совокупность. Для такого агрегата 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 строится со всеми соответствующими конструкторами, выполненными на месте. Никакие карты или строки или MyClasses никогда не копируются и не перемещаются. Обратите внимание, что оба варианта внизу внизу делают то же самое. Вы даже можете сделать копию MyClass и переместить конструкторы, если хотите.

Ответ 2

Это не сводная инициализация вообще, это равномерная инициализация, которая в основном в этом случае означает вызов конструктора, а no copy или move, вероятно, выполняются RVO и NRVO.