Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How come I can't use explicit constructor for constructing a return type

I played around with explicit constructors and their behavior, so I created this class:

#include <iostream>

class X
{
public:
    explicit X(void)
    {
        std::cout << "Default constructor\n";
    }
    explicit X(X const& x)
    {
        std::cout << "Copy constructor\n";
    }
    explicit X(X&& x)
    {
        std::cout << "Move constructor\n";
    }
};

Which is basically just a stub to to test explicit constructors. Then I wanted to try several situations. So I tried this:

X foo(void)
{
    X a{};
    return a;  // ERROR: no matching constructor found!
}

int main()
{
    X w{};  // Default Constructor
    X x{w};  // Copy Constructor
    X y{std::move(x)};  // Move Constructor
    X z{foo()};
}

And as you can see I can't return a inside foo(). I know it tries to initialize the return type Foo with the copy constructor, but for some reason it can't use it.

How come it can't use my provided copy constructor? I know that the explicit specification causes the problem, because when I remove it from the copy constructor it works. But why?

What confuses me even more is that I can do the following:

void bar(const X& a) { /* */ }
bar(X{});

It doesn't complain. But shouldn't bar() construct it's parameter a the same way foo() constructs its return type?

like image 734
hgiesel Avatar asked Feb 28 '16 21:02

hgiesel


2 Answers

When you are returning from foo:

X foo()
{
    X a{};
    return a;
}

You are implicitly copying a into the return of foo. But the copy constructor is marked explicit, so that is disallowed.

I'm not sure there is ever a reason to mark the copy/move constructors explicit.

like image 63
Barry Avatar answered Sep 30 '22 14:09

Barry


I think you've misunderstood the meaning of explicit. An explicit constructor WILL NOT BE USED FOR IMPLICIT TYPE CONVERSIONS/CASTS. This means the

X foo(void){
    X a{};
    return a;  // ERROR: no matching constructor found!
}

won't compile since you've already told the compiler not to use the copy constructor implicitly.

I reckon that what you want to achieve is "move" rather than copy a. As long as there is a (normal) move constructor in your class, a will be moved rather than copied anyway - this is the default behaviour. Actually, even with c++99, most compilers are clever enough to optimise out this copy anyway.

like image 39
Marinos K Avatar answered Sep 30 '22 14:09

Marinos K