2016-05-12 2 views
39

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

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

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

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

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

  1. Почему у меня нет ошибок при компиляции?
  2. Как заставить компилятор помочь мне обнаружить это? (Первоначально структура была написана, чтобы иметь bool данных, но по прошествии некоторого времени, код изменился и стал boolint и были введены несколько ошибок.)
+1

Я удивлен, что второй разрешен, но я получаю то же самое (без ошибок). – wally

+18

Существует целая поддержка сохранения ценности от 'bool' до' int', которая применяется неявно. Инициализация скобок только плохо сформирована для * сужения * конверсий. –

+0

:(Да, я тоже, поскольку, надеюсь, компилятор мне поможет. –

ответ

70

bool может быть неявно преобразован в int в способе, которым это значение сохранения. Единственными запрещенными преобразованиями с инициализацией скобки являются narrowing conversions (например, обратный 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.

+4

Да, это лучше. –

+1

Очень красивый, хороший момент, что 'is_constructible' не учитывает удаленные перегрузки. –

+2

«сужение» - это неправильное употребление ... это означает, что один уже, чем другой, однако «int-> double» и «double-> int» считаются «сужающимися». Может быть, «lossy» будет лучшим описанием –

7

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

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

+0

Я пробовал и «явный» не мешает этому. Не нужно ли «явное» во встроенном int? :) – wally

+1

@flatmouse: Haha damn –

+0

@LightnessRacesinOrbit Итак, не удалось поймать такого рода сотрудников во время компиляции? –

15

Вот конструкция, которая позволяет только инициализировать класс от 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) {} 

    // ... 
+0

Теперь это должно быть «Хорошо!». –

+3

Я знаю, что вопрос отмечен как C++ 11, но я думаю, что стоит упомянуть, что 'static_assert' не существует в предыдущих версиях. – Michael

+0

@ Майкл: Это правда. Версия SFINAE также может использоваться на C++ 98 (пока вы сами пишете признак). –

8

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

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. 
+1

Да, но теперь вы должны сделать это для всех других вещей, которые преобразуются в 'int'. –

+0

@LightnessRacesinOrbit true, но я изо всех сил стараюсь увидеть это как проблему. Компилятор поймет сужение конверсий, если OP использует скобку-инициализацию в соответствии с его примером. И если ему нужны конкретные конструкторы для каждого типа int, тогда ему придется писать конструкторы (или шаблоны) в любом случае. –

+1

Будет ли скобка-инициализация ловить 'short'->' int' conversion? Это уже еще один конструктор, который вам нужно вручную «удалить». И как вы знаете, что ваши пользователи будут использовать инициализацию brace? Не получил последнее предложение; OP хочет _one_ рабочий конструктор. –

0

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

Смежные вопросы