Explicit constructor taking multiple arguments

C++Explicit Constructor

C++ Problem Overview


Does making a constructor having multiple arguments explicit have any (useful) effect?

Example:

class A {
    public:
        explicit A( int b, int c ); // does explicit have any (useful) effect?
};

C++ Solutions


Solution 1 - C++

Up until C++11, yeah, no reason to use explicit on a multi-arg constructor.

That changes in C++11, because of initializer lists. Basically, copy-initialization (but not direct initialization) with an initializer list requires that the constructor not be marked explicit.

Example:

struct Foo { Foo(int, int); };
struct Bar { explicit Bar(int, int); };

Foo f1(1, 1); // ok
Foo f2 {1, 1}; // ok
Foo f3 = {1, 1}; // ok

Bar b1(1, 1); // ok
Bar b2 {1, 1}; // ok
Bar b3 = {1, 1}; // NOT OKAY

Solution 2 - C++

You'd stumble upon it for brace initialization (for instance in arrays)

struct A {
        explicit A( int b, int c ) {}
};

struct B {
         B( int b, int c ) {}
};

int main() {
	B b[] = {{1,2}, {3,5}}; // OK
	
	A a1[] = {A{1,2}, A{3,4}}; // OK
	
	A a2[] = {{1,2}, {3,4}}; // Error
	
	return 0;
}

Solution 3 - C++

The excellent answers by @StoryTeller and @Sneftel are the main reason. However, IMHO, this makes sense (at least I do it), as part of future proofing later changes to the code. Consider your example:

class A {
    public:
        explicit A( int b, int c ); 
};

This code doesn't directly benefit from explicit.

Some time later, you decide to add a default value for c, so it becomes this:

class A {
    public:
        A( int b, int c=0 ); 
};

When doing this, you're focussing on the c parameter - in retrospect, it should have a default value. You're not necessarily focussing on whether A itself should be implicitly constructed. Unfortunately, this change makes explicit relevant again.

So, in order to convey that a ctor is explicit, it might pay to do so when first writing the method.

Solution 4 - C++

Here's my five cents to this discussion:

struct Foo {
    Foo(int, double) {}
};

struct Bar {
    explicit Bar(int, double) {}
};

void foo(const Foo&) {}
void bar(const Bar&) {}

int main(int argc, char * argv[]) {
    foo({ 42, 42.42 }); // valid
    bar({ 42, 42.42 }); // invalid
    return 0;
}

As you can easily see, explicit prevents from using initializer list alongside with bar function bacause the constructor of struct Bar is declared as explicit.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPeter G.View Question on Stackoverflow
Solution 1 - C++SneftelView Answer on Stackoverflow
Solution 2 - C++StoryTeller - Unslander MonicaView Answer on Stackoverflow
Solution 3 - C++Ami TavoryView Answer on Stackoverflow
Solution 4 - C++Edgar RokjānView Answer on Stackoverflow