What does the explicit keyword mean?

1,051,390

Solution 1

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.

Solution 2

Suppose, you have a class String:

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

Now, if you try:

String mystring = 'x';

The character 'x' will be implicitly converted to int and then the String(int) constructor will be called. But, this is not what the user might have intended. So, to prevent such conditions, we shall define the constructor as explicit:

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};

Solution 3

In C++, a constructor with only one required parameter is considered an implicit conversion function. It converts the parameter type to the class type. Whether this is a good thing or not depends on the semantics of the constructor.

For example, if you have a string class with constructor String(const char* s), that's probably exactly what you want. You can pass a const char* to a function expecting a String, and the compiler will automatically construct a temporary String object for you.

On the other hand, if you have a buffer class whose constructor Buffer(int size) takes the size of the buffer in bytes, you probably don't want the compiler to quietly turn ints into Buffers. To prevent that, you declare the constructor with the explicit keyword:

class Buffer { explicit Buffer(int size); ... }

That way,

void useBuffer(Buffer& buf);
useBuffer(4);

becomes a compile-time error. If you want to pass a temporary Buffer object, you have to do so explicitly:

useBuffer(Buffer(4));

In summary, if your single-parameter constructor converts the parameter into an object of your class, you probably don't want to use the explicit keyword. But if you have a constructor that simply happens to take a single parameter, you should declare it as explicit to prevent the compiler from surprising you with unexpected conversions.

Solution 4

The keyword explicit accompanies either

  • a constructor of class X that cannot be used to implicitly convert the first (any only) parameter to type X

C++ [class.conv.ctor]

1) A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters to the type of its class. Such a constructor is called a converting constructor.

2) An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used. A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or valueinitialization (8.5).

  • or a conversion function that is only considered for direct initialization and explicit conversion.

C++ [class.conv.fct]

2) A conversion function may be explicit (7.1.2), in which case it is only considered as a user-defined conversion for direct-initialization (8.5). Otherwise, user-defined conversions are not restricted to use in assignments and initializations.

Overview

Explicit conversion functions and constructors can only be used for explicit conversions (direct initialization or explicit cast operation) while non-explicit constructors and conversion functions can be used for implicit as well as explicit conversions.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

Example using structures X, Y, Z and functions foo, bar, baz:

Let's look at a small setup of structures and functions to see the difference between explicit and non-explicit conversions.

struct Z { };

struct X { 
  explicit X(int a); // X can be constructed from int explicitly
  explicit operator Z (); // X can be converted to Z explicitly
};

struct Y{
  Y(int a); // int can be implicitly converted to Y
  operator Z (); // Y can be implicitly converted to Z
};

void foo(X x) { }
void bar(Y y) { }
void baz(Z z) { }

Examples regarding constructor:

Conversion of a function argument:

foo(2);                     // error: no implicit conversion int to X possible
foo(X(2));                  // OK: direct initialization: explicit conversion
foo(static_cast<X>(2));     // OK: explicit conversion

bar(2);                     // OK: implicit conversion via Y(int) 
bar(Y(2));                  // OK: direct initialization
bar(static_cast<Y>(2));     // OK: explicit conversion

Object initialization:

X x2 = 2;                   // error: no implicit conversion int to X possible
X x3(2);                    // OK: direct initialization
X x4 = X(2);                // OK: direct initialization
X x5 = static_cast<X>(2);   // OK: explicit conversion 

Y y2 = 2;                   // OK: implicit conversion via Y(int)
Y y3(2);                    // OK: direct initialization
Y y4 = Y(2);                // OK: direct initialization
Y y5 = static_cast<Y>(2);   // OK: explicit conversion

Examples regarding conversion functions:

X x1{ 0 };
Y y1{ 0 };

Conversion of a function argument:

baz(x1);                    // error: X not implicitly convertible to Z
baz(Z(x1));                 // OK: explicit initialization
baz(static_cast<Z>(x1));    // OK: explicit conversion

baz(y1);                    // OK: implicit conversion via Y::operator Z()
baz(Z(y1));                 // OK: direct initialization
baz(static_cast<Z>(y1));    // OK: explicit conversion

Object initialization:

Z z1 = x1;                  // error: X not implicitly convertible to Z
Z z2(x1);                   // OK: explicit initialization
Z z3 = Z(x1);               // OK: explicit initialization
Z z4 = static_cast<Z>(x1);  // OK: explicit conversion

Z z1 = y1;                  // OK: implicit conversion via Y::operator Z()
Z z2(y1);                   // OK: direct initialization
Z z3 = Z(y1);               // OK: direct initialization
Z z4 = static_cast<Z>(y1);  // OK: explicit conversion

Why use explicit conversion functions or constructors?

Conversion constructors and non-explicit conversion functions may introduce ambiguity.

Consider a structure V, convertible to int, a structure U implicitly constructible from V and a function f overloaded for U and bool respectively.

struct V {
  operator bool() const { return true; }
};

struct U { U(V) { } };

void f(U) { }
void f(bool) {  }

A call to f is ambiguous if passing an object of type V.

V x;
f(x);  // error: call of overloaded 'f(V&)' is ambiguous

The compiler does not know wether to use the constructor of U or the conversion function to convert the V object into a type for passing to f.

If either the constructor of U or the conversion function of V would be explicit, there would be no ambiguity since only the non-explicit conversion would be considered. If both are explicit the call to f using an object of type V would have to be done using an explicit conversion or cast operation.

Conversion constructors and non-explicit conversion functions may lead to unexpected behaviour.

Consider a function printing some vector:

void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }

If the size-constructor of the vector would not be explicit it would be possible to call the function like this:

print_intvector(3);

What would one expect from such a call? One line containing 3 or three lines containing 0? (Where the second one is what happens.)

Using the explicit keyword in a class interface enforces the user of the interface to be explicit about a desired conversion.

As Bjarne Stroustrup puts it (in "The C++ Programming Language", 4th Ed., 35.2.1, pp. 1011) on the question why std::duration cannot be implicitly constructed from a plain number:

If you know what you mean, be explicit about it.

Solution 5

This answer is about object creation with/without an explicit constructor since it is not covered in the other answers.

Consider the following class without an explicit constructor:

class Foo
{
public:
    Foo(int x) : m_x(x)
    {
    }

private:
    int m_x;
};

Objects of class Foo can be created in 2 ways:

Foo bar1(10);

Foo bar2 = 20;

Depending upon the implementation, the second manner of instantiating class Foo may be confusing, or not what the programmer intended. Prefixing the explicit keyword to the constructor would generate a compiler error at Foo bar2 = 20;.

It is usually good practice to declare single-argument constructors as explicit, unless your implementation specifically prohibits it.

Note also that constructors with

  • default arguments for all parameters, or
  • default arguments for the second parameter onwards

can both be used as single-argument constructors. So you may want to make these also explicit.

An example when you would deliberately not want to make your single-argument constructor explicit is if you're creating a functor (look at the 'add_x' struct declared in this answer). In such a case, creating an object as add_x add30 = 30; would probably make sense.

Here is a good write-up on explicit constructors.

Share:
1,051,390
Skizz
Author by

Skizz

I first started programming aged nine in Basic on a Sinclair ZX80, quickly progressing onto Z80 machine code on the ZX Spectrum. This led to the PC platform, an Amstrad 1512, where I learned various flavours of BASIC and 8086 programming, even doing some very rudimentary 3D graphics. Up until this point, everything had been self taught. In 1988 I went to Bradford University to study for an Electronic / Software Engineering degree. This was a thin sandwich style course over four years, the first three were split six months at university and six months on placement in industry. The first year introduced the Pascal programming language and the Unix operating system at university and C during the placement. Subsequent years concentrated on C and assembly language for various platforms – mainly Motorola and Transputer based as well as Sun Unix systems. Upon completing my degree I developed and had published my first PC game. This led the way to further jobs in the games industry and it wasn't long before C++ became the language of choice for work. In 2004 I began branching out on my own to work on business solutions. My interest in programming goes beyond work related projects. I spend my spare time looking into new technologies and new languages. I have recently configured a linux server for my home network, built some small, personal websites and tools to update the databases that sit behind the sites. I purchased a Raspberry Pi which I use to develop linux based software using python, OpenGL|ES, etc., as well as building some electronic circuits to interface to the device.

Updated on April 20, 2022

Comments

  • Skizz
    Skizz about 2 years

    What does the explicit keyword mean in C++?

    • chris
      chris over 11 years
      I just want to point out to anyone new coming along that ever since C++11, explicit can be applied to more than just constructors. It's now valid when applied to conversion operators as well. Say you have a class BigInt with a conversion operator to int and an explicit conversion operator to std::string for whatever reason. You'll be able to say int i = myBigInt;, but you'll have to cast explicitly (using static_cast, preferably) in order to say std::string s = myBigInt;.
    • Eitan Myron
      Eitan Myron about 10 years
      Can't explicit also refer to assignment? (i.e. int x(5);)
    • curiousguy
      curiousguy almost 6 years
      @chris The idea of an explicit implicit conversion is absurd. Stay clear of it!
    • chris
      chris almost 6 years
      @curiousguy, There's no such thing as an explicit implicit conversion.
    • curiousguy
      curiousguy almost 6 years
      @chris There is an explicit keyword that can be used on the declaration of an implicit conversion.
    • chris
      chris almost 6 years
      @curiousguy, It's not inherently an implicit conversion. Putting explicit there declares an explicit conversion to a type. No implicitness involved in the process.
    • curiousguy
      curiousguy almost 6 years
      @chris Explicit conversion is an ill defined concept.
    • Sz.
      Sz. over 4 years
      @curiousguy: What do you mean? All conversions should be implicit? To let loose all sorts of silently applied funny miscasts due to accidental ambiguities? (See e.g. the "The safe bool problem" section at this C++ref. page, or open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2333.html for (much) more details on why "Explicit conversion is an ill defined concept." is an ill-conceived statement.)
    • curiousguy
      curiousguy over 4 years
      @Sz. I mean that explicit conversion is not a thing; it's a garbage concept. Also the "safe bool" is a ridiculous "proof" for the usefulness of the "explicit operator" idea as it isn't even an application of that idea but a different set of rules, which means the only practical use of "explicit operator" in the SL is ad hoc and not applicable to UDT.
    • Milan
      Milan over 3 years
      @chris did you mean like this: std::string s = static_cast<std::string>(myBigInt) ? If possible, could you please elaborate furthermore on your first comment? Thanks a lot in advance!
    • chris
      chris over 3 years
      @Milan, Yes, that's exactly it. If you're looking for more information, this answer writes it up more formally. Do note that bool is special in this regard. Those answers and searching "explicit conversion operators" will lead you to more writeups about this feature and be better suited than a comment chain.
  • 0xCAFEBABE
    0xCAFEBABE over 15 years
    nice write up, you might want to mention multi-arg ctors with default params can also act as single arg ctor, e.g., Object( const char* name=NULL, int otype=0).
  • Michael Burr
    Michael Burr over 14 years
    I think it should also be mentioned that one should consider making single argument constructors explicit initially (more or less automatically), and removing the explicit keyword only when the implicit conversion is wanted by design. I think contructors should be explicit by default with an 'implicit' keyword to enable them to work as implicit conversions. But that's not how it is.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 13 years
    And it's worth noting that the new generalized initialization rules of C++0x will make String s = {0}; ill-formed, rather than trying to call the other constructor with a null pointer, as String s = 0; would do.
  • Christian Severin
    Christian Severin almost 13 years
    @thecoshman: You don't declare a parameter explicit -- you declare a constructor explicit. But yes: your parameters of type Foo have to be constructed explicitely, they won't be silently constructed by just plugging their constructor's parameters into the function.
  • Larry
    Larry almost 12 years
    Just an FYI that when calling "print(3)" in your example, the function needs to be "print(const MyString &"). The "const" is mandatory here because 3 is converted to a temporary "MyString" object and you can't bind a temporary to a reference unless it's "const" (yet another in a long list of C++ gotchas)
  • Arbalest
    Arbalest over 11 years
    For completeness sake, I am adding that in addition to parameter conversion the explicit keyword here will also prevent the use of assignment form of a copy ctor (e.g., Foo myFoo = 42;) and require the explicit forms Foo myFoo = Foo(42); or Foo myFoo(42);
  • Arbalest
    Arbalest over 10 years
    Even though this is an old question it seems worth pointing a few things out (or having someone set me straight). By making the int form, or both ctors, 'explicit' you would still have the same bug if you used String mystring('x') when you meant String mystring("x") wouldn't you? Also, from the comment above I see the improved behavior of String s = {0} over String s = 0 thanks to making the int form of the ctor 'explicit'. But, other than knowing the precedence of the ctors how do you know the intent (i.e. how to spot the bug) of this String s{0} ?
  • 6502
    6502 about 10 years
    C c(); in the first example doesn't mean what you think it means: it's the declaration of a function named c that takes no parameters and returns an instance of C.
  • InQusitive
    InQusitive about 9 years
    Why String mystring = 'x'; is getting converted to int?
  • DavidRR
    DavidRR almost 9 years
    @InQusitive: 'x'is being treated as an integer because the char data type is just a 1-byte integer.
  • Maggyero
    Maggyero over 8 years
    The problem with your example is that it only works with copy initialization (using =) but not with direct initialization (without using =): the compiler will still call the String(int) constructor without generating an error if you write String mystring('x');, as @Arbalest pointed out. The explicit keyword is meant for preventing implicit conversions that happen in direct initialization and function resolution. A better solution to your example would be a simple overload of the constructor: String(char c);.
  • v010dya
    v010dya over 8 years
    This post is written in 2009. Today you don't declare them as private, but rather say = delete.
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica about 8 years
    explicit operator bool() is also the C++11 version of safe bool, and can be used implicitly in condition checks (and only in condition checks, as far as I'm aware). In your second example, this line would also be valid in main(): if (c) { std::cout << "'c' is valid." << std:: endl; }. Apart from this, though, it can't be used without explicit casting.
  • pqnet
    pqnet almost 7 years
    useBuffer expects an lvalue for his argument, useBuffer(Buffer(4)) will not work either because of it. Changing it to take a const Buffer& or Buffer&& or just Buffer would make it work.
  • curiousguy
    curiousguy almost 6 years
    explicit operator bool() vs. if is a special case. There is no way to reproduce it with user defined Bool, explicit operator Bool() and a function called If.
  • curiousguy
    curiousguy almost 6 years
    "constructor to be called explicitly" no
  • curiousguy
    curiousguy almost 6 years
    @JustinTime It's an inane, broken version of the safe bool. The whole idea of explicit implicit conversion is absurd.
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica almost 6 years
    @curiousguy True. It seems a bit like a kludge, aimed more at being easily remembered (likely in the hopes of that translating to frequently used) than at following English logic, and designed to not be outright incompatible with previous safe bool implementations (so you're less likely to break something if you swap it in). IMO, at least.
  • mosegui
    mosegui about 3 years
    But, if I understand your latter counter-example correctly, print("3") should yield a compiling error since the function print is actually expecting a MyString instance, and not an actual string, and the constructor of the MyString class does not allow the compiler to automatically cast the string "3" to a MyString object. Am I understanding it wrong?
  • Skizz
    Skizz about 3 years
    @mosegui: yes, you're right, as given in the answer. I should have said that there was an overload of print that took a char *. I'll modify the answer.
  • JDługosz
    JDługosz over 2 years
    I edited this to fix the C c(); and also played around in Compiler Explorer and it appears that making a default constructor explicit doesn't actually do anything. This answer is simply wrong, even with the code corrections already made.
  • Skizz
    Skizz over 2 years
    @66Gramms: Your changes to the code in this answer only make it more readable to you, to some people, including me, the code is less readable. Please don't edit answers to change the coding style to suit your preferences.