Почему предупреждение о сужении конверсии появляется только в случае инициализации списка?

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

class A
{
    public:
        A(const unsigned int val) : value(val) {}

        unsigned int value;
};

int main()
{
    int val = 42;
    A a(val);
    A b{val};       // <--- Warning in GCC, error in Microsoft Visual Studio 2015

    return 0;
}

Почему предупреждение об уменьшении сужения появляется только в случае использования инициализации списка?

Ответы

Ответ 1

инициализация списка была введена с С++ 11 с функцией, запрещающей неявное сужение конверсий между встроенными типами. В то же время две другие формы "старого стиля" (начиная с С++ 98), которые используют круглые скобки и знак равенства, например

int val = 42;
A a(val);
A a = val;

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

Ответ 2

В соответствии со стандартом сужение конверсий в этом контексте является незаконным. В другом контексте они являются законными. (Под "незаконным", я имею в виду сделать программу плохо сформированной).

Стандарт требует, чтобы компилятор выдал диагностику в этом конкретном случае (сделать программу плохо сформированной). Что делает компилятор после испускания диагностики, стандарт оставляет undefined.

MSVC решает прекратить компиляцию. Gcc выбирает испускать носовых демонов притворяться, что программа имеет смысл, выполняет преобразование и продолжает компилироваться.

Оба предупреждения и ошибки являются диагностикой в ​​отношении стандарта. Традиционно ошибки - это то, что вы называете диагностикой, которая предшествует компиляции компилятора.

Также обратите внимание, что компиляторы могут свободно испускать диагностику, когда захотят.

Традиционно предупреждения используются, когда вы делаете что-то, что стандартное диктует, - это хорошо сформированная программа, но авторы компилятора считают, что не рекомендуется, и ошибки, когда стандарт обнаруживает плохо сформированную программу, но большинство компиляторов не применяют это строго.

Ответ 3

Причина предупреждения уже объясняется другими ответами.

Вот как исправить это предупреждение/ошибку. Создайте конструктор, который принимает значение initializer_list в качестве аргумента.

A(std::initializer_list<int> l) : value(*(l.begin())) { 
    cout << "constructor taking initializer list called\n";
}