What Does the Explicit Keyword Mean

What does the explicit keyword mean?

The compiler is allowed to make one implicit conversion to resolve the parameters to a function. What this means is that the compiler can use constructors callable with a single parameter to convert from one type to another in order to get the right type for a parameter.

Here's an example class with a constructor that can be used for implicit conversions:

class Foo
{
private:
int m_foo;

public:
// single parameter constructor, can be used as an implicit conversion
Foo (int foo) : m_foo (foo) {}

int GetFoo () { return m_foo; }
};

Here's a simple function that takes a Foo object:

void DoBar (Foo foo)
{
int i = foo.GetFoo ();
}

and here's where the DoBar function is called:

int main ()
{
DoBar (42);
}

The argument is not a Foo object, but an int. However, there exists a constructor for Foo that takes an int so this constructor can be used to convert the parameter to the correct type.

The compiler is allowed to do this once for each parameter.

Prefixing the explicit keyword to the constructor prevents the compiler from using that constructor for implicit conversions. Adding it to the above class will create a compiler error at the function call DoBar (42). It is now necessary to call for conversion explicitly with DoBar (Foo (42))

The reason you might want to do this is to avoid accidental construction that can hide bugs.

Contrived example:

  • You have a MyString class with a constructor that constructs a string of the given size. You have a function print(const MyString&) (as well as an overload print (char *string)), and you call print(3) (when you actually intended to call print("3")). You expect it to print "3", but it prints an empty string of length 3 instead.

What is the explicit keyword for in c++? [duplicate]

It's use to decorate constructors; a constructor so decorated cannot be used by the compiler for implicit conversions.

C++ allows up to one user-provided conversion, where "user-provided" means, "by means of a class constructor", e.g., in :

 class circle {
circle( const int r ) ;
}

circle c = 3 ; // implicit conversion using ctor

the compiler will call the circle ctor here, constructinmg circle c with a value of 3 for r.

explicit is used when you don't want this. Adding explicit means that you'd have to explicitly construct:

 class circle {
explicit circle( const int r ) ;
}

// circle c = 3 ; implicit conversion not available now
circle c(3); // explicit and allowed

usage of explicit keyword for constructors

explicit is intended to prevent implicit conversions. Anytime you use something like String(foo);, that's an explicit conversion, so using explicit won't change whether it succeeds or fails.

Therefore, let's look at a scenario that does involve implicit conversion. Let's start with your String class:

class String {
public:
explicit String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};

Then let's define a function that receives a parameter of type String (could also be String const &, but String will do for the moment):

int f(String);

Your constructors allow implicit conversion from char const *, but only explicit conversion from int. This means if I call:

f("this is a string");

...the compiler will generate the code to construct a String object from the string literal, and then call f with that String object.

If, however, you attempt to call:

f(2);

It will fail, because the String constructor that takes an int parameter has been marked explicit. That means if I want to convert an int to a String, I have to do it explicitly:

f(String(2));

If the String(char const *); constructor were also marked explicit, then you wouldn't be able to call f("this is a string") either--you'd have to use f(String("this is a string"));

Note, however, that explicit only controls implicit conversion from some type foo to the type you defined. It has no effect on implicit conversion from some other type to the type your explicit constructor takes. So, your explicit constructor that takes type int will still take a floating point parameter:

f(String(1.2))

...because that involves an implicit conversion from double to int followed by an explicit conversion from int to String. If you want to prohibit a conversion from double to String, you'd do it by (for example) providing an overloaded constructor that takes a double, but then throws:

String(double) { throw("Conversion from double not allowed"); }

Now the implicit conversion from double to int won't happen--the double will be passed directly to your ctor without conversion.

As to what using explicit accomplishes: the primary point of using explicit is to prevent code from compiling that would otherwise compile. When combined with overloading, implicit conversions can lead to some rather strange selections at times.

It's easier to demonstrate a problem with conversion operators rather than constructors (because you can do it with only one class). For example, let's consider a tiny string class fairly similar to a lot that were written before people understood as much about how problematic implicit conversions can be:

class Foo {
std::string data;
public:
Foo(char const *s) : data(s) { }
Foo operator+(Foo const &other) { return (data + other.data).c_str(); }

operator char const *() { return data.c_str(); }
};

(I've cheated by using std::string to store the data, but the same would be true if I did like they did and stored a char *, and used new to allocate the memory).

Now, this makes things like this work fine:

Foo a("a");
Foo b("b");

std::cout << a + b;

...and, (of course) the result is that it prints out ab. But what happens if the user makes a minor mistake and types - where they intended to type +?

That's where things get ugly--the code still compiles and "works" (for some definition of the word), but prints out nonsense. In a quick test on my machine, I got -24, but don't count on duplicating that particular result.

The problem here stems from allowing implicit conversion from String to char *. When we try to subtract the two String objects, the compiler tries to figure out what we meant. Since it can't subtract them directly, it looks at whether it can convert them to some type that supports subtraction--and sure enough, char const * supports subtraction, so it converts both our String objects to char const *, then subtracts the two pointers.

If we mark that conversion as explicit instead:

explicit operator char const *() { return data.c_str(); }

...the code that tries to subtract two String objects simply won't compile.

The same basic idea can/does apply to explicit constructors, but the code to demonstrate it gets longer because we generally need at least a couple of different classes involved.

Why is the 'explicit' keyword allowing implicit conversions?

explicit refers to the constructor itself, not the constructor's parameters. Your explicit constructor may not be used as an implicit conversion to type Test.

void function( Test param );

function( 5 ); // Your "explicit" makes this call an error.
// The parameter must be explicitly cast, such as Test(5)

In C++11 or later, you can prevent implicit parameter conversions using the = delete syntax.

  Test(int i)
{
value=i;
}

template<typename T>
Test(const T&) = delete;
// ^ Aside from your int constructor and the implicitly-generated
// copy constructor, this will be a better match for any other type

In C++20 or later, you can prevent implicit parameter conversions using the std::same_as concept.

  Test(std::same_as<int> auto i)
{
value=i;
}

Using explicit constructor [duplicate]

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.

Application of C++ Explicit Constructor [duplicate]

An explicit constructor is a function that does not get called in implicit type conversion.

For example:

class A {
A( int a ) {}
};

void foo( A a ) {}

Here is totally legal to call foo(1) or use any variable of type int or that can be implicitly converted to an int. This is not always desirable, as it would mean that A is convertible from an integer, instead of being defined with an integer. Adding the explicit would avoid the conversion and, hence, give you a compilation error.

Does explicit keyword have any effect on a default constructor?

Yes, it does have an effect.

Compare:

struct A
{
A() {}
};

void foo(A) {}

int main()
{
foo({}); // ok
}

and:

struct A
{
explicit A() {}
};

void foo(A) {}

int main()
{
foo({}); // error
}


Related Topics



Leave a reply



Submit