Using explicit constructor

8.8k views Asked by At
class foo {
public:
    explicit foo(int) { std::cout<<"int constructor"; }
}; 

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

I thought explicit keyword was being used to prevent unwanted type conversions, but the above code still works and outputs "int constructor". Why? And how to prevent that in this case?

3

There are 3 answers

0
HolyBlackCat On BEST ANSWER

You're confusing some concepts here.

explicit prevents implicit constructor calls, not constructor calls with implicit parameter type conversions.

Unfortunately the solution is not straightforward. It's not quite newbie-friendly and may require some googling to understand.

You could expicitly delete the constructors for floating-point types: foo(float) = delete; and so on. Then foo f(0.1); will cause 'use of deleted function' error. But you won't be able to do foo f(1l) either, the compiler will complain about 'ambiguous overloads'.

The proper way is following:

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;
}; 

It's similar to deleting overloads for each floating-point type (as described above), but due to SFINAE, the deleted overload won't be considered for non-floating-point types.

0
Saurav Sahu On

Keyword explicit enforces a compile-time error with message as conversion from ‘int’ to non-scalar type ‘foo’ requested, when you try to do an implicit conversion like this :foo f = 1;. That's all it's expected to do.

Why does it allow float value 0.1 is answered here.

Futhermore if you want to prevent this behavior, use this line of code:

foo(double) = delete;

then you would get an error: use of deleted function ‘foo::foo(double)’ while passing a float/double value.

0
Nir Friedman On

An explicit constructor prevents implicit conversion into your type, from the type taken by the constructor. So in this case, it prevents implicit conversion from int into foo. So:

foo f = 1;

Will not compile. However, this is explicit construction:

foo f(1);

This, of course will work. In your case, you passed a double instead of an int. But there's an implicit conversion from double to int built into the language, that's why it compiles. In other words, your problem is that this compiles:

int x = 0.1;

However, compiling with -Wconversion on gcc (and I believe clang) will raise a warning for this, and if you are compiling with -Werror (which you should), it will turn this into a compilation error. I suspect MSVC has a similar error if you are working with that compiler.