Подтвердить что ты не робот

Предотвращение сужения конверсии при использовании std:: initializer_list

#include <iostream>

struct X {
    X(std::initializer_list<int> list) { std::cout << "list" << std::endl; }
    X(float f) { std::cout << "float" << std::endl; }
};

int main() {
    int x { 1.0f };
    X a(1);     // float (implicit conversion)
    X b{1};     // list
    X c(1.0f);  // float
    X d{1.0f};  // list (narrowing conversion) ARG!!!

    // warning: narrowing conversion of '1.0e+0f' from 'float' to 'int'
    // inside { } [-Wnarrowing]
}

Есть ли другой способ удаления std::initializer_list из списка перегрузки (т.е. сделать более непригодным для списка) вместо использования() -инициализации или, по крайней мере, запретить сужение конверсии (кроме поворота предупреждение в ошибке)?

Я использовал компилятор http://coliru.stacked-crooked.com/, который использует GCC 4.8.

4b9b3361

Ответ 1

На самом деле, программа, содержащая сужение преобразования в инициализаторе списка скобок, плохо сформирована. Я не уверен, почему компилятор просто дает вам предупреждение, но он определенно должен выпустить ошибку здесь (FWIW, Clang делает это).

Также обратите внимание, что это сужение (и, следовательно, незаконное) преобразование:

int x { 1.0f }; // ERROR! Narrowing conversion required

В пункте 8.5.4/3 стандарта С++ 11:

Список-инициализация объекта или ссылки типа T определяется следующим образом:

- Если T является агрегатом, выполняется агрегатная инициализация (8.5.1). [...]

- В противном случае, если в списке инициализаторов нет элементов [...]

- В противном случае, если T является специализацией std::initializer_list<E>, [...]

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

Чтобы быть более точным, Стандарт только говорит, что в этом случае требуется "диагностика", а предупреждение - это диагностика, поэтому поведение компилятора соответствует, но я считаю, что испускание ошибки было бы лучшим поведением.

Ответ 2

Это похоже на ошибку компилятора. Вы должны получить сообщение об ошибке вместо предупреждения. Инициализация брекета никогда не должна быть явно ограничена.

Из стандарта (§ 8.5.4)

struct B {
  B(std::initializer_list<int>);
};
B b1 { 1, 2 }; // creates initializer_list<int> and calls constructor
B b2 { 1, 2.0 }; // error: narrowing

Ответ 3

Вы можете достичь того, чего хотите с помощью std::enable_if.

#include <iostream>
#include <type_traits>

struct X {
    template<typename T, typename = typename std::enable_if<std::is_same<T,int>::value>::type>
    X(std::initializer_list<T>) { std::cout << "list" << std::endl; }
    X(float) { std::cout << "float" << std::endl; }
};

int main() {
    X a(1);     // float (implicit conversion)
    X b{1};     // list
    X c(1.0f);  // float
    X d{1.0f};  // float (yay)
}

Работает как с g++ 4.8 и clang 3.2

Ответ 4

Вы можете использовать -Wno-c++11-narrowing, чтобы отключить ошибки:

Вот пример тестовой программы:

#include <cstdint>

struct foo {
    int32_t a;
};

void foo(int64_t val) {
    struct foo A = { val };
}

Скомпилируйте с clang++ - 3.8 только с -std=c++11, мы получим указанную ошибку:

введите описание изображения здесь

Добавить -Wno-c++11-narrowing, золотая тишина: -)

введите описание изображения здесь

Конечно, сужение вопроса может вернуться, чтобы укусить вас позже, но иногда может быть легче отложить боль в технической задолженности до конца. ymmv: -)