2016-02-28 2 views
6

я играл вокруг с явными конструкторами и их поведением, так что я создал этот класс:Почему я не могу использовать явный конструктор для построения возвращаемого типа

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

который в основном только заглушки для того чтобы испытать явные конструкторы. Тогда я хотел попробовать несколько ситуаций. Так что я попытался это:

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

И как вы можете видеть, что я не могу вернуться a внутри foo(). Я знаю, что он пытается инициализировать возвращаемый тип Foo с помощью конструктора копирования, но по какой-то причине он не может его использовать.

Почему он не может использовать предоставленный мной конструктор копирования? Я знаю, что спецификация explicit вызывает проблему, потому что когда я удаляю ее из конструктора копирования, она работает. Но почему?

Что меня смущает еще то, что я могу сделать следующее:

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

Он не жалуется. Но не должно bar() построить его параметр a таким же образом foo() строит свой тип возврата?

+1

вызов 'bar' вообще не вызывает конструктор, он просто передает ссылку (указатель). вы должны увидеть, что вызовы no copy не вызываются. –

ответ

5

Когда вы возвращаетесь из foo:

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

Вы неявно копируя a в возвращение foo. Но конструктор копирования помечен explicit, так что это запрещено.

Я не уверен, что когда-либо есть причина отметить конструкторы копирования/перемещения explicit.

+0

Но я все еще могу использовать define 'void bar (const X & a)' и использовать его как этот 'bar (X {});'. Разве это тоже неявная копия? – hgiesel

+0

@henrikgiesel Nope. 'a' является ссылкой, которую вы просто связываете с временным' X {} '. – Barry

+0

Хорошо, я наконец получил картинку, когда переписал 'X & foo()', и это сработало. (Конечно, это глупо, но это помогло мне понять, что происходит) – hgiesel

0

Вы не можете, потому что вы сказали, что не хотите, чтобы компилятор использовал его неявно, когда вы объявили его явным.

explicit X(X const& x) 

Но x должен быть скопирован в возвращаемое значение. Просто измените его на

X(X const& x) 

и все будет работать.

Live on Coliru

+0

* «Я знаю, что явная спецификация вызывает проблему, потому что когда я удаляю ее из конструктора копирования, она работает. Но почему?» * - OP. – LogicStuff

+1

Это значит, что в аду я не могу заставить 'foo()' возвращать неповрежденный объект 'Foo'? – hgiesel

+0

В любом случае, чтобы он работал с 'explicit'? –

1

Я думаю, вы неправильно поняли значение explicit. Конструктор explicit НЕ ИСПОЛЬЗУЕТСЯ ДЛЯ НЕПРЕРЫВНЫХ ТИПОВЫХ КОНВЕРСИЙ/СЛУЧАЙ. Это означает, что

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

не будет компилироваться, так как вы уже сказали, компилятор не использовать конструктор копирования неявно.

Я считаю, что вы хотите достичь «переместить», а не копировать a. Пока в вашем классе есть (нормальный) механизм перемещения, a будет перемещен, а не скопирован - это поведение по умолчанию. На самом деле, даже с C++ 99, большинство компиляторов достаточно умны, чтобы в любом случае оптимизировать эту копию.