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

Я определил структуру, которая имеет конструктор:

struct MyStruct
{
    MyStruct(const int value)
        : value(value)
    {
    }
    int value;
};

и следующие объекты:

int main()
{
    MyStruct a (true);
    MyStruct b {true};
}

Но я не получил ошибок компиляции, либо с MVS2015, либо с Xcode 7.3.1.

  • Почему я не получаю ошибок компиляции?
  • Как мне заставить компилятор помочь мне это обнаружить? (Первоначально структура была записана с данными bool, но через некоторое время код был изменен, а bool стал int и было введено несколько ошибок.)

Ответ 1

A bool может быть неявно преобразован в int таким образом, что сохранение значения. Единственными запрещенными преобразованиями с инициализацией скобки являются сужение конверсий (например, обратное bool{42}).

Если вы хотите, чтобы ваш класс был конструктив только с int, прямой путь - это просто delete все остальные конструкторы:

struct MyStruct
{
    explicit MyStruct(int i) : value(i) { }

    template <typename T>
    MyStruct(T t) = delete;

    int value;
};

Здесь MyStruct{true} и MyStruct(false) будут давать вызовы MyStruct::MyStruct<bool>, которые определены как удаленные и, следовательно, плохо сформированы.

Преимущество этого над static_assert заключается в том, что все черты типа будут давать правильные значения. Например, std::is_constructible<MyStruct, bool> - std::false_type.

Ответ 2

Здесь построена конструкция, которая позволяет инициализировать ваш класс только с помощью значения int:

#include <type_traits>

struct MyStruct
{
    template <typename T>
    MyStruct(T t) : value(t)
    {
        static_assert(std::is_same<T, int>::value, "Bad!");
    }

    int value;
};

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

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

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

Другими словами, я бы написал его так:

struct MyStruct
{
    template <typename T,
              typename = std::enable_if_t<std::is_same<T, int>::value>>
    MyStruct(T t) : value(t) {}

    // ...

Ответ 3

Самое простое решение - объявить конструктор bool как удаленный, не так ли?

struct MyStruct
{
    MyStruct(bool) = delete;

    MyStruct(const int value)
    : value(value)
    {
    }
    int value;
};

Пример вывода ошибки:

...
/Users/xxxxxxx/play/fast_return/skeleton/main.cpp:68:14: error: call to deleted constructor of 'MyStruct'
    MyStruct b {true};
             ^ ~~~~~~
/Users/xxxxxxx/play/fast_return/skeleton/main.cpp:57:9: note: 'MyStruct' has been explicitly marked deleted here
        MyStruct(bool) = delete;
        ^
2 errors generated.

Ответ 4

Поскольку bool может быть неявно преобразован в int.

Это языковая функция, которую вы не можете отключить, извините.

Ответ 5

значение true преобразуется в значение 1 (int).