Пересылка в агрегатный инициализатор? - PullRequest
0 голосов
/ 09 февраля 2019

Мне кажется, что агрегатная инициализация (подходящих типов) не считается конструктором, который вы можете вызвать (кроме нескольких случаев).

Например, если у нас очень простой агрегатный тип:

struct Foo {
    int x, y;
};

тогда, очевидно, это работает:

auto p = new Foo {42, 17};  // not "Foo (42, 17)" though...

, но это не работает ни на одном из протестированных мной компиляторов (включая последние версии MSVC, GCC и Clang):

std::vector<Foo> v;
v.emplace_back(2, 3);

Опять же, мне кажется, что любой код, который хочет вызвать конструктор для типа T (в данном случае, код в vector::emplace_back, который передает переданные аргументы в cT,) не может использовать агрегатную инициализацию просто (кажется), потому что они используют скобки вместо фигурных скобок!

Почему это так?Это просто пропущенная функция (никто еще не предложил / не реализовал ее) или есть более глубокие причины?Это немного странно, потому что агрегатные типы по определению не имеют другого конструктора, который делает разрешение неоднозначным, поэтому язык мог бы просто определить конструктор агрегатов по умолчанию (или что-то в этом роде), в котором все члены были бы аргументами по умолчанию.

Это просто вопрос синтаксиса?Если бы реализация vector::emplace_back в вышеприведенном примере использовала размещение new с фигурными скобками вместо скобок, сработало бы это?

Примечание : Я хочу поблагодарить те комментарии, которыеЯ указал на поведение vector и emplace, потому что их комментарии будут полезны тем, кто найдет этот вопрос по этим ключевым словам, но я также хочу отметить, что это всего лишь примеры.Я выбрал самый знакомый и лаконичный пример, но моя точка зрения касалась явного вызова агрегатного инициализатора в любом коде (или в placement new, точнее.)

Ответы [ 2 ]

0 голосов
/ 09 февраля 2019

https://en.cppreference.com/w/cpp/language/aggregate_initialization

Агрегатная инициализация не является конструктором.

Согласно этому документу вы не определили никаких конструкторов и отвечаете другим условиям агрегированной инициализации.(См. Пункт «тип класса» в разделе «Объяснение».) Это означает, что ваш код не вызывает нечто вроде автоматически сгенерированного конструктора подписи Foo(int, int), но это просто еще одна особенность.

В документе говорится оего эффект:

Каждый элемент массива или нестатический член класса в порядке индекса / появления массива в определении класса инициализируется копией из соответствующего предложения списка инициализаторов.

Так как vector::emplace_back(Args&&... args) работает таким образом и не может найти такой конструктор.

Аргументы args... передаются в конструктор как std::forward<Args>(args)....

Таким образом, он не находит такого конструктора.

С этой точки зрения также имеет смысл, что ваш код не может скомпилировать auto p = new Foo (42, 17);.

Еще один пример,если вы пишете конструктор любого типа (даже Foo::Foo() {}), auto p = new Foo {42, 17}; не работает.Потому что теперь он не соответствует условию агрегированной инициализации.

Насколько я знаю, агрегированная инициализация также работает в C, который даже не поддерживает конструкторы.

Вот хорошая статья стоит прочесть.

0 голосов
/ 09 февраля 2019

Для чего стоит, P0960 «Разрешить инициализацию агрегатов из заключенного в скобки списка значений» делает именно то, что говорит.Похоже, что передал EWG и находится на пути к C ++ 20.

агрегатные типы по определению не имеют другого конструктора, чтобы сделать разрешение неоднозначным

Это неверно.Все классы имеют конструкторы по умолчанию, а также конструкторы копирования / перемещения.Даже если вы = delete их или они неявно удалены, у них все же есть такие конструкторы (вы просто не можете их вызывать).

C ++ - это C ++, естественно, существуют угловые случаи, когда даже P0960 делает "неправильно », как указано в статье:

struct A;

struct C
{
  operator A(); //Implicitly convertible to `A`
};

struct A { C c; }; //First member is a `C`

C c2;
A a(c2);

Инициализация a - это случай двусмысленности.Две вещи могут произойти.Вы можете выполнить неявное преобразование c2 в A, а затем инициализировать a из полученного значения.Или вы можете выполнить агрегатную инициализацию a с помощью одного значения типа C.

P0960 выбирает обратно совместимый маршрут: если конструктор мог бы быть вызван (в соответствии с существующими правилами)тогда это всегда имеет приоритет.Круглые скобки вызывают только агрегатную инициализацию, если нет конструктора, который мог бы быть вызван.

...