Использование явного конструктора

class foo {
public:
    explicit foo(int) { std::cout<<"int constructor"; }
}; 

int main() {
    foo f(0.1);
    return 0;
}

Я думал, что явное ключевое слово используется для предотвращения нежелательных преобразований типов, но приведенный выше код все еще работает и выводит «конструктор int». Почему? И как этого не допустить в этом случае?


person J. Doe    schedule 25.08.2017    source источник
comment
main() (без возвращаемого типа) — синтаксическая ошибка в C++.   -  person melpomene    schedule 25.08.2017
comment
Моя беда, отредактирую пост.   -  person J. Doe    schedule 25.08.2017
comment
Это точно обман? Редактировать: неправильно понял.   -  person Nir Friedman    schedule 25.08.2017
comment
explicit предотвращает неявное создание объекта foo из объекта int, например f = 1;. Это будет компилироваться без ключевого слова explicit в конструкторе foo(int).   -  person Michael Burr    schedule 25.08.2017


Ответы (3)


Вы тут путаете некоторые понятия.

explicit предотвращает неявные вызовы конструктора, а не вызовы конструктора с неявными преобразованиями типов параметров.

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

Вы можете явно удалить конструкторы для типов с плавающей запятой: foo(float) = delete; и так далее. Тогда foo f(0.1); вызовет ошибку «использование удаленной функции». Но вы также не сможете выполнить foo f(1l), компилятор будет жаловаться на "неоднозначные перегрузки".

Правильный способ следующий:

class foo
{
  public:
    foo(int) {std::cout << "int constructor\n";}
    template <typename T,
              typename = std::enable_if_t<std::is_floating_point_v<T>>>
    foo(T) = delete;
}; 

Это похоже на удаление перегрузок для каждого типа с плавающей запятой (как описано выше), но из-за SFINAE удаленная перегрузка не будет учитываться для типов без плавающей запятой.

person HolyBlackCat    schedule 25.08.2017

Явный конструктор предотвращает неявное преобразование в ваш тип из типа, взятого конструктором. Так что в этом случае это предотвращает неявное преобразование из int в foo. Так:

foo f = 1;

Не компилируется. Однако это явная конструкция:

foo f(1);

Это, конечно, сработает. В вашем случае вы передали double вместо int. Но в язык встроено неявное преобразование из double в int, поэтому он компилируется. Другими словами, ваша проблема в том, что это компилируется:

int x = 0.1;

Однако компиляция с помощью -Wconversion на gcc (и, я думаю, clang) вызовет предупреждение об этом, а если вы компилируете с -Werror (что и следует делать), это превратит это в ошибку компиляции. Я подозреваю, что MSVC имеет аналогичную ошибку, если вы работаете с этим компилятором.

person Nir Friedman    schedule 25.08.2017

Ключевое слово explicit вызывает ошибку времени компиляции с сообщением conversion from ‘int’ to non-scalar type ‘foo’ requested, когда вы пытаетесь выполнить неявное преобразование, подобное этому: foo f = 1;. Это все, что ожидается.

Почему это позволяет значение с плавающей запятой 0.1 отвечает здесь .

Кроме того, если вы хотите предотвратить такое поведение, используйте эту строку кода:

foo(double) = delete;

то вы получите сообщение об ошибке: use of deleted function ‘foo::foo(double)’ при передаче значения float/double.

person Saurav Sahu    schedule 25.08.2017