2016-02-28 16 views
6

Tôi chơi xung quanh với nhà thầu rõ ràng và hành vi của họ, vì vậy tôi tạo ra lớp này:Tại sao tôi không thể sử dụng constructor rõ ràng cho việc xây dựng một kiểu trả về

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

cơ bản mà chỉ là một stub để kiểm tra rõ ràng nhà thầu. Sau đó, tôi muốn thử một số tình huống. Vì vậy, tôi cố gắng này:

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

Và như bạn có thể thấy tôi không thể trở về a bên foo(). Tôi biết nó cố gắng khởi tạo kiểu trả về Foo với hàm tạo bản sao, nhưng vì một số lý do nó không thể sử dụng nó.

Làm sao nó không thể sử dụng hàm tạo bản sao được cung cấp của tôi? Tôi biết rằng các đặc điểm kỹ thuật explicit gây ra vấn đề, bởi vì khi tôi loại bỏ nó từ các nhà xây dựng bản sao nó hoạt động. Nhưng tại sao?

gì confuses me thậm chí nhiều hơn là tôi có thể làm như sau:

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

Nó không phàn nàn. Nhưng không nên bar() xây dựng thông số của nó a giống như cách foo() tạo kiểu trả về của nó?

+1

gọi 'bar' không gọi hàm tạo nào cả, nó chỉ chuyển tham chiếu (con trỏ). bạn sẽ thấy các constructor không copy được gọi. –

Trả lời

5

Khi bạn đang trở về từ foo:

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

Bạn đang ngầm sao chép a vào sự trở lại của foo. Nhưng hàm tạo bản sao được đánh dấu explicit, do đó không được phép.

Tôi không chắc chắn có bao giờ là lý do để đánh dấu các nhà xây dựng sao chép/di chuyển explicit.

+0

Nhưng tôi vẫn có thể sử dụng 'void bar (const X & a)' và sử dụng nó như thế này 'bar (X {});'. Đây không phải là một bản sao ngầm? – hgiesel

+0

@henrikgiesel Không. 'a' là một tham chiếu, mà bạn chỉ gắn với' X {} 'tạm thời. – Barry

+0

Được rồi, cuối cùng tôi đã có hình ảnh khi tôi viết lại 'X & foo()' và nó đã hoạt động. (Tất nhiên đó là ngu ngốc, nhưng nó đã giúp tôi hiểu những gì đang xảy ra) – hgiesel

0

Bạn không thể vì bạn đã nói rằng bạn không muốn trình biên dịch sử dụng nó hoàn toàn khi bạn khai báo rõ ràng.

explicit X(X const& x) 

Nhưng x phải được sao chép vào giá trị trả lại. Chỉ cần thay đổi nó thành

X(X const& x) 

và mọi thứ sẽ hoạt động.

Live on Coliru

+0

* "Tôi biết rằng các đặc điểm kỹ thuật rõ ràng gây ra vấn đề, bởi vì khi tôi loại bỏ nó từ các nhà xây dựng bản sao nó hoạt động. Nhưng tại sao?" * - OP. – LogicStuff

+1

Điều đó có nghĩa là, không có cách nào trong địa ngục tôi có thể làm cho 'foo()' trả về một đối tượng 'Foo' nguyên vẹn? – hgiesel

+0

Dù sao để làm cho nó hoạt động với 'tường minh'? –

1

Tôi nghĩ bạn đã hiểu sai ý nghĩa của explicit. Một nhà xây dựng explicit S NOT KHÔNG ĐƯỢC SỬ DỤNG ĐỐI VỚI CÁC LOẠI CHUYỂN ĐỔI LOẠI/CASTS. Điều này có nghĩa là

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

sẽ không biên dịch vì bạn đã yêu cầu trình biên dịch không sử dụng hàm tạo bản sao hoàn toàn.

Tôi cho rằng những gì bạn muốn đạt được là "di chuyển" thay vì sao chép a. Miễn là có một hàm khởi tạo (bình thường) trong lớp của bạn, thì a sẽ được di chuyển thay vì sao chép - đây là hành vi mặc định. Trên thực tế, ngay cả với c + + 99, hầu hết các trình biên dịch đủ thông minh để tối ưu hóa ra bản sao này anyway.

Các vấn đề liên quan