Почему в этом примере я получаю ошибку C2797?

У меня есть следующий код:

struct X
{
    int a, b;
};

class Y
{
public:
    Y(const X& x) : x_{x} {};  // C2797 error

private:
    X x_;
};

Скомпилированный с обновлением 3 MSVC2013, он жалуется на ошибку C2797. Если я заменю фигурные скобки круглыми скобками (например, x_(x)), программа успешно скомпилируется.

Почему это происходит? Является ли это поведение компилятора совместимым с С++ 11? А как насчет С++14?

Редактировать: Чтобы быть более ясным, я не уверен, должен ли x_{x} выше, в соответствии со стандартом, вызывать X(std::initializer_list) или это допустимый синтаксис для вызова X(const X&). Насколько я знаю, это последнее. Я прав?


person Dan Nestor    schedule 25.10.2014    source источник
comment
Да. Если вы подразумеваете, что ответ там, я этого не понял, и я был бы признателен, если бы вы указали это в ответе здесь.   -  person Dan Nestor    schedule 26.10.2014
comment
@Dan: вы используете инициализацию списка {} и делаете это внутри списка инициализаторов членов. В сообщении об ошибке говорится, что это допустимо, но компилятор не готов позволить вам это сделать. Вместо этого попробуйте x_(x) в качестве обходного пути, это прямая инициализация, а не инициализация списка.   -  person Ben Voigt    schedule 26.10.2014
comment
@Ben: Итак, если я правильно понимаю, вы говорите, что инициализация {} была бы законной, но она просто не реализована в этом компиляторе?   -  person Dan Nestor    schedule 26.10.2014
comment
@ Дэн: Да. Скорее всего, та часть компилятора, которая решает, допустимо ли это в данном конкретном случае, тоже еще не реализована. Поскольку ваш struct X является агрегатом, я бы использовал фигурные скобки {} для инициализации агрегата (особая форма инициализации списка) и () для построения копии, что у вас есть здесь.   -  person Ben Voigt    schedule 26.10.2014
comment
@Бен: Спасибо. Пожалуйста, сформулируйте это как краткий ответ.   -  person Dan Nestor    schedule 26.10.2014
comment
Кажется, gcc тоже жалуется: ideone.com/pbzNzm   -  person Dan Nestor    schedule 26.10.2014
comment
Возможный дубликат stackoverflow.com/questions/21906792   -  person Moby Disk    schedule 04.11.2014


Ответы (2)


Из стандарта:

— Если T является агрегатом, выполняется агрегатная инициализация.

[...]

— В противном случае, если T — тип класса, рассматриваются конструкторы. Перечисляются применимые конструкторы, и лучший из них выбирается с помощью разрешения перегрузки (13.3, 13.3.1.7). Если для преобразования какого-либо из аргументов требуется сужающее преобразование (см. ниже), программа некорректна.

В приведенном выше контексте x_{x} не будет вызывать конструктор копирования, поскольку X является агрегатом. Он попытается выполнить агрегатную инициализацию, которая:

  • В MSVC не реализовано. MSVC также, по-видимому, терпит неудачу при компиляции, когда X равно std::string, что не является агрегатом, поэтому у него могут быть проблемы с совместимостью с C++11.

  • В gcc это реализовано, но программа имеет неправильный формат и не может скомпилироваться (попытка инициализировать агрегат, ожидающий {int, int} из {const X}.

person Dan Nestor    schedule 26.10.2014

Как вы заметили, gcc действительно знает, что означает этот синтаксис, выдает конкретное сообщение об ошибке:

cannot convert ‘const X’ to ‘int’ in initialization

Это связано с тем, что фигурные скобки {} запускают инициализацию списка и инициализацию списка для агрегатного типа (ваш struct X является агрегатом, если вы не знаете почему, просто подумайте: «как массив, он просто содержит данные без какого-либо собственного поведения ") выполняет агрегатную инициализацию. Агрегатная инициализация означает, что инициализаторы сопоставляются с элементами данных по порядку, а любые дополнительные члены данных получают инициализированное значение.

x_.a связан с x x_.b не связан ни с чем, поэтому значение инициализировано

Это не то, что вам нужно, потому что вы не можете вставить все x в x_.a (о чем вам говорит gcc). То, что вы действительно хотели, это прямая инициализация с использованием конструктора копирования, записанного как _x(x).

В Visual C++ история немного другая. Инженеры Microsoft все еще работают над добавлением поддержки C++11, и это одна из вещей, которую они еще не закончили (по крайней мере, в вашей версии). Компилятор знает, что когда он видит {} в списке инициализаторов ctor, это означает инициализацию списка, но он не знает, как это сделать, поэтому сдается.

В частности, он не доходит до того, чтобы увидеть, что X является агрегатом, соединить инициализаторы с элементами данных и выяснить, возможно ли спаривание x_.a с x.

Когда компилятор говорит вам, что "это не реализовано", это не означает, что код хороший, это не значит, что код плохой. Это означает, что для компиляции этого кода нужна логика, которая еще не выпущена (может быть, еще не написана, может быть еще не протестирована, никто за пределами Microsoft не знает). Ваш код был доставлен на завод, загружен на ленту конвейера, начал двигаться по сборочной линии и... упал с конца ленты, потому что машины, которая работает с таким кодом, еще нет на заводе. . Никто не знает, если бы машина была там, чтобы подобрать его, бросила бы он его в стопку ошибок или получила бы на другом конвейере.

person Ben Voigt    schedule 25.10.2014
comment
Я предполагаю, что мне непонятно следующее: x_{x} это не инициализация списка, не так ли? Согласно единым правилам инициализации, этот синтаксис должен вызывать (определяемый компилятором) конструктор копирования X, то есть X(const X&), не X(std::initializer_list). Эта часть синтаксиса языка C++ неправильно анализируется ни gcc, ни MSVC. - person Dan Nestor; 26.10.2014
comment
Я добавил правку в свой вопрос, чтобы уточнить ответ, который я ищу. - person Dan Nestor; 26.10.2014