2010-05-05 44 views
11
class Foo 
{ 
public: 
    explicit Foo() {} 
    explicit Foo(Foo&) {} 
}; 

Foo d = Foo(); 

error: no matching function for call to 'Foo::Foo(Foo)'Foo f = Foo(); // không có hàm nào phù hợp để gọi đến 'Foo :: Foo (Foo)' ... huh?

tôi đã cố gắng thay đổi Foo(Foo&)-Foo(Foo) như lỗi thấy, mà AFAIK không phải là một nhà xây dựng có giá trị, và đủ chắc chắn tôi nhận được:

error: invalid constructor; you probably meant ‘Foo (const Foo&)’

gì cho? Làm cách nào để giải quyết vấn đề này? (Đây là trên GCC bằng cách này)

+1

Foo Foo Foo? Huh? –

+2

Trình biên dịch đã trả lời câu hỏi của bạn ... 'Foo (const Foo &)'. 'Foo d = Foo();' đang gọi hàm tạo bản sao. –

+3

+1 bởi vì không ai có thể biết câu trả lời –

Trả lời

12

Có hai điều đáng ngờ bạn có trong trình tạo bản sao của mình.

Trước tiên, bạn đã thực hiện các bản sao-constructor rõ ràng (mà là một điều đáng ngờ để làm), vì vậy bạn sẽ (về mặt lý thuyết) cần thực hiện:

Foo d((Foo())); 

Thứ hai, xây dựng bản sao của bạn mất một tham chiếu chứ không phải tham chiếu const có nghĩa là bạn không thể sử dụng nó với một tạm thời Foo.

Cá nhân, tôi chỉ cần xóa explicit từ trình tạo bản sao và làm cho tham chiếu const nếu có thể.

Lưu ý rằng explicit trên hàm tạo mặc định của bạn không có hiệu lực. [*] explicit chỉ có tác dụng đối với các nhà thầu có thể được gọi với một tham số duy nhất. Nó ngăn cản chúng được sử dụng cho các chuyển đổi tiềm ẩn. Đối với các hàm tạo chỉ lấy 0 hoặc chỉ có hai hoặc nhiều tham số, nó không có hiệu lực.

[Lưu ý: có thể có một sự khác biệt giữa:.

Foo d; 

Foo d = Foo(); 

nhưng trong trường hợp này, bạn có một constructor mặc định sử dụng tuyên bố như thế này không áp dụng]

Chỉnh sửa: [*] Tôi vừa kiểm tra lại điều này và 12.3.1 [class.conv.ctor] nói rằng bạn có thể tạo điều khoản mặc định tructor explicit. Trong trường hợp này, hàm tạo sẽ được sử dụng để thực hiện khởi tạo mặc định hoặc khởi tạo giá trị.Thành thật mà nói, tôi không hiểu giá trị của điều này như thể bạn có một constructor do người dùng khai báo thì đó là kiểu không phải POD và thậm chí cả các đối tượng cục bộ kiểu không POD được khởi tạo mặc định nếu chúng không có initializer điều khoản này nói có thể được thực hiện bởi một hàm dựng mặc định là explicit. Có lẽ ai đó có thể chỉ ra một trường hợp góc mà nó làm cho một sự khác biệt nhưng bây giờ tôi không thấy những gì có hiệu lực explicit có trên một constructor mặc định.

+1

Tại sao phải đặt thêm dấu ngoặc đơn? – clahey

+2

@clahey: Để có được xung đột khó hiểu nhất. –

+4

Bây giờ James đã đưa ra câu trả lời đúng, tôi có thể nói: bởi vì tôi là một người hâm mộ tủ quần áo. –

2

Sự cố của bạn đang diễn ra. Bạn không cần Foo d = Foo(); cho hàm tạo mặc định.

Giữ lớp học của bạn như nhau, nhưng cố gắng này cho instantiation:

Foo d; 

Trong thực tế, bạn thậm chí không cần Foo d = Foo(arguments); cho xây dựng với các thông số. Đó nên là như thế này:

Foo d(arguments); 
+0

'Foo d = Foo()' chỉ đơn giản là tạo ra lỗi. Tôi có thể đã có 'Foo d;' và 'Foo k = d; 'ở ​​một nơi khác – Kyle

+0

@Kyle: Từ nhiều bình luận bạn đã để lại, tôi có thể thấy bạn đang cố tình gọi ra hàm tạo bản sao. Tôi ước bạn đã nói một cách rõ ràng trong câu hỏi của bạn. Tôi không thể tìm thấy bất cứ nơi nào trong câu hỏi của bạn mà bạn ngụ ý rằng bạn thậm chí biết những gì một nhà xây dựng bản sao * là *; bạn vừa đăng mã và nói "tại sao nó lại là brokens?" Tôi đã đưa ra giả định (vâng, vâng) rằng bạn chỉ đơn giản là làm điều đó sai. 'Foo d = Foo();' chắc chắn gọi hàm tạo bản sao trên cá thể 'Foo' thoáng qua, nhưng đó là thứ bạn không nên thực sự * làm *. – Randolpho

+0

Như ngụ ý trong tiêu đề của nó, câu hỏi của tôi là liên quan đến thông báo lỗi kỳ lạ, mà dường như không phải là một kết quả rõ ràng từ mã đã cho. Tôi vẫn chưa nắm vững kỹ năng dồn vào một câu hỏi chính xác đến nỗi không ai có thể trả lời được câu hỏi sai. Xin vui lòng chỉnh sửa câu trả lời của bạn để SO sẽ cho phép tôi để upvote bạn. – Kyle

2
Foo d = Foo(); 

nên

Foo d; 

Dòng đầu tiên tạo ra một trường hợp Foo và sau đó sao chép nó vào d;

+0

Đúng, nhưng không phải là câu trả lời cho câu hỏi - 'Foo d = Foo()' chỉ đơn giản là tạo ra lỗi. Tôi có thể có 'Foo d;' và 'Foo k = d; 'ở ​​một nơi khác. – Kyle

3

Thử không rõ ràng? Tôi nghĩ rằng:

Foo foo = Foo() 

tạo bản sao ngầm, do đó trình tạo bản sao rõ ràng không được kích hoạt.

Chỉnh sửa:

Đây chỉ là một nửa câu trả lời. Xem Charles Bailey hoặc UncleBens đăng bài vì sao const là cần thiết.

+1

Đây là câu trả lời đúng. Hơn nữa, không có lý do gì để làm cho hàm tạo bản sao rõ ràng để bắt đầu. Nhà xây dựng rõ ràng có nghĩa là để giữ cho bạn khỏi vô tình, các loại chuyển đổi ngầm. –

+1

Đây là một nửa câu trả lời. – clahey

1

Trình biên dịch là nói cho bạn ... Sử dụng này:

Foo(const Foo&) {} 
+0

Đã bỏ phiếu xuống. Nó là khá nghĩa đen nói những gì chữ ký sẽ như thế nào. (Không phải những gì nó nên làm - không có gì - mặc dù.) – UncleBens

5

Bạn không muốn đánh dấu một trong những nhà xây dựng như rõ ràng - trình biên dịch cần phải sử dụng cả trong số họ, đặc biệt là các nhà xây dựng bản sao, ngầm. Bạn đang cố gắng đạt được điều gì bằng cách đánh dấu chúng rõ ràng?

+0

Để trả lời câu hỏi của bạn: Tôi muốn loại bỏ bất kỳ suprises gây ra bởi một chuyển đổi tôi không muốn xảy ra (... bằng cách có trình biên dịch nâng cao một lỗi). – Kyle

+1

@Kyle Nhưng bạn muốn trình biên dịch có thể tạo bản sao - đó không phải là một chuyển đổi. Và không phải là xây dựng mặc định. –

+0

Phải, nhưng tôi muốn nó đánh bom nếu tôi cố gắng làm 'Foo f = Bar()' nơi 'Bar' vui vẻ cho phép bản thân được chuyển đổi thành một Foo bằng cách cung cấp một toán tử Foo()' (tôi nghĩ mình có quá trình suy nghĩ đó đúng ..) – Kyle

0

Bạn có thể khắc phục sự cố theo một trong hai cách. Một (đã được đề xuất bởi Randolpho) là loại bỏ bằng cách sử dụng ctor sao chép. Cách khác là viết một ctor sao chép thích hợp:

Foo (Foo const &) {} 

Bạn thường muốn làm cả hai.

Chỉnh sửa: Nhìn vào nó, nhận xét cuối cùng của tôi rất dễ hiểu sai. Khá ít lớp học làm không cần một bản sao ctor, nhưng nếu bạn cần một bản sao ctor, nó thường có dạng ở trên (không rõ ràng và tham chiếu const làm tham số).

+0

'Foo (Foo &)' là một hàm tạo bản sao thích hợp. Nó chỉ đơn giản là không phải là nhà xây dựng bản sao chính xác cho những gì anh ta đang cố gắng làm. –

+0

@ Dennis: Nếu bạn thích nó, điều đó là tốt - nhưng một nhà xây dựng bản sao mở ra cánh cửa để sửa đổi đối tượng đang được sao chép không phải là thứ tôi gọi là "thích hợp". –

-1
class Foo 
{ 
public: 
    explicit Foo() {} 
    explicit Foo(const Foo&) {} 
}; 

Foo d = Foo() 
+0

lý do phủ định 1? – Betamoo

+0

không phải tôi, nhưng tôi có thể nói với bạn rằng vẫn không biên dịch. – Kyle

3

Một constructor sao chép không nên rõ ràng (mà làm cho nó uncallable đây và trong nhiều bối cảnh hoàn toàn hợp lý khác, chẳng hạn như khi đi qua hoặc trở lại theo giá trị).

Tiếp theo, bạn cần tham số bằng cách tham chiếu const, vì nếu không nó không thể liên kết với thời gian.

Foo f = Foo(); 
     ^^^^^ 
      | 
      --- this is a temporary that cannot be passed to a function 
       that accepts a non-const reference 

Bên cạnh đó, không có lý do gì để làm cho các nhà xây dựng mặc định rõ ràng: từ khoá này chỉ có ý nghĩa đối với nhà xây dựng (trừ các nhà xây dựng bản sao) mà có thể được gọi với chính xác một đối số, trong trường hợp này nó ngăn cản chuyển đổi ngầm của các loại khác thành Foo thông qua hàm tạo đó.Ví dụ, nếu một constructor lấy một intrõ ràng, tình huống như thế này sẽ không biên dịch:

Foo f; 
f = 1; //assuming no operator= overload for (types convertible from) int 
     //this implicitly performs f = Foo(1); 

Foo g = 10; 

void x(Foo); 
x(20); 

Tất cả trong tất cả:

class Foo 
{ 
public: 
    Foo(); 
    Foo(const Foo&); 
    //... 
}; 

Foo x = Foo(); 

Và hơn nữa, nếu không ai trong số những nhà xây dựng có nghĩa là làm bất cứ điều gì, bạn không cần phải định nghĩa chúng - trình biên dịch sẽ cung cấp chúng tự động (nếu bạn định nghĩa bất kỳ hàm tạo khác nào, hàm tạo mặc định sẽ không được tạo tự động).

+0

Đây là câu trả lời hay nhất cho đến nay, nhưng tôi sẽ thêm nhiều văn bản hơn về những việc cần làm là gì. – clahey

+0

Bạn có thể gọi hàm tạo bản sao rõ ràng bằng cách khởi tạo trực tiếp (ví dụ: 'Foo f; Foo g (f);'). –

+0

@James: Cảm ơn, đã làm rõ thêm. – UncleBens

4

Đầu tiên, không phải hàm tạo mặc định cũng như hàm tạo bản sao nào phải là explicit. Bạn chỉ cần tạo một hàm tạo explicit nếu nó lấy một đối số duy nhất của một số loại khác, để ngăn chặn chuyển đổi ngầm từ loại đó. Trình tạo bản sao có tham chiếu đến chính lớp đó, do đó không có nguy cơ chuyển đổi không mong muốn.

Thứ hai, hãy đảm bảo rằng trình tạo bản sao có tham chiếu const.

Thứ ba, Foo f; là cách phù hợp để có đối tượng được tạo mặc định là lớp foo. Lưu ý rằng Foo f(); là sai, bởi vì trình biên dịch sẽ giải thích rằng như một khai báo hàm f() trả về một đối tượng của lớp Foo.

Thứ tư, nếu bạn đã viết trình tạo bản sao của riêng mình, thì bạn cũng nên viết toán tử gán.

 

class Foo 
{ 
    Foo() {} // no need to make explicit. Nothing to convert from. 

    Foo(const &Foo f) {} // again, nothing wrong with conversion from Foo to Foo 

    explicit Foo(int a) {} // need explicit to prevent accidental passing of an int 
          // to a function that takes Foo as an argument 
}; 
 
Các vấn đề liên quan