2010-01-05 40 views
7

Xét đoạn mã sau:C++ nhà xây dựng rõ ràng và lặp

Nên các biên dịch trên? Cảm giác của tôi là nó không nên, do các nhà xây dựng được đánh dấu explicit.

Microsoft Visual C++ đồng ý, đưa ra một thông báo lỗi rõ ràng: cannot convert from 'int' to 'const A'; Constructor for struct 'A' is declared 'explicit'

Tuy nhiên, sử dụng Comeau's online compiler, mã biên dịch thành công.

Điều gì là chính xác?

Edit:

Điều thú vị là, thay đổi vector-set (sau khi thêm một operator < đến A) gây ra cả hai trình biên dịch để đưa ra một lỗi.

Tuy nhiên, thay đổi vector<int> thành map<int, int>vector<A> đến map<A, A> khiến cả hai trình biên dịch chấp nhận mã!

+0

Trả lời gián tiếp tại đây: http://stackoverflow.com/questions/1943228/implicit-constructor-conversion-works-on-explicit-vectorvector-only-sometimes –

+1

Tôi vừa viết câu trả lời ở đây dựa trên câu hỏi đó + câu trả lời, nhưng nó thực sự không áp dụng, vì 'std :: vector :: iterator' không phải là một kiểu không thể tách rời, và fill-constructor không được gọi. Vì vậy, tôi đã xóa nó. Tôi nghĩ rằng tất cả những gì tôi có thể nói là tiêu chuẩn không * cấm * chuyển đổi rõ ràng trong bất kỳ nhà xây dựng vùng chứa nào. – Potatoswatter

+0

VS 2010 Beta hoạt động như Comeau và gcc. –

Trả lời

3

Tôi đã xem xét việc thực hiện STL của GCC và cần có hành vi tương tự. Đây là lý do tại sao.

  • yếu tố của một vector được khởi tạo bởi một mẫu hàm tổng quát mà chấp nhận bất kỳ hai loại XV và gọi new(p) X(v) nơi v là một V (tôi diễn giải một chút). Điều này cho phép chuyển đổi rõ ràng.
  • Các yếu tố của một số set hoặc map được khởi tạo bởi chức năng thành viên riêng của _tree<T,…>, đặc biệt mong đợi T const & được truyền vào. Chức năng thành viên này không phải là mẫu (ngoài thành viên của mẫu), vì vậy nếu ban đầu giá trị không thể được chuyển đổi hoàn toàn thành T, cuộc gọi không thành công. (Một lần nữa tôi đơn giản hóa mã.)

Tiêu chuẩn không yêu cầu chuyển đổi rõ ràng hoặc chuyển đổi ngầm không hoạt động khi khởi tạo vùng chứa có phạm vi. Nó chỉ đơn giản nói rằng phạm vi được sao chép vào container. Chắc chắn mơ hồ cho mục đích của bạn.

Đáng ngạc nhiên sự mơ hồ đó tồn tại, xem xét cách họ đã tinh chỉnh tiêu chuẩn trong việc xem xét các sự cố như the one I had cách đây vài tuần.

1

Tôi nghĩ rằng nó sẽ phụ thuộc vào cách std::vector<A> As(Iterator,Iterator) được triển khai trong việc triển khai STL cụ thể của bạn.

+0

Hành vi của thư viện chuẩn không phải là phụ thuộc vào việc thực hiện. –

+1

Sau đó, một lần nữa, có những nơi (rất hiếm), nơi tiêu chuẩn có thể được giải thích theo hai cách không tương đương. –

0

Mã này không biên dịch trong Comeau: Thông điệp

class Foo 
{ 
public: 
explicit Foo(int bar) 
{ 
} 
}; 

class Bar 
{ 
void DoStuff(Foo foo){ 

} 
void DoStuff2() 
{ 
    DoStuff(4); 
} 
}; 

Lỗi:

"ComeauTest.c", line 16: error: no suitable constructor exists to convert from "int" 
      to "Foo" 
    DoStuff(4); 
      ^

1 error detected in the compilation of "ComeauTest.c". 

Vì vậy, ở mức thô sơ trình biên dịch trực tuyến hỗ trợ nhà xây dựng rõ ràng. Phải là một cái gì đó để làm với vector/iterators.

EDIT Tuy nhiên điều này biên dịch:

Foo foo = (Foo)5; 

Đó là một chuyển đổi rõ ràng, vì vậy đó là OK. Tôi đoán lớp vector Comeau thực hiện một phép thuật rõ ràng trong hàm tạo ở đâu đó, thư viện của Microsoft không ở đâu.

Thông tin thêm về nhà xây dựng rõ ràng - http://www.glenmccl.com/tip_023.htm

0

Có cần biên dịch. Nếu người xây dựng không được sử dụng, thì nhân chứng của nó không phải là vấn đề.

+0

Nếu hàm khởi tạo không được sử dụng, chúng ta sẽ chuyển đổi từ int thành A như thế nào? – user200783

+0

Không có gì đang được tạo và không có gì đang được chuyển đổi - cả hai vectơ trống rỗng –

+0

Hành vi là giống nhau nếu một cái gì đó được chèn vào vectơ "ints" trước khi xây dựng "As". – user200783

1

Đây là một câu hỏi khá phức tạp, và có thể là trường hợp VisualStudio là đúng và Comeau sai (điều này có vẻ thực sự khó tin).

Tiêu chuẩn nếu đọc từng chữ, xác định hàm tạo vector theo phương thức của hàm dựng sao chép (xem báo giá) và điều đó nghĩa là đối tượng thu được bằng dereferencing trình biến đổi trước tiên phải được chuyển đổi thành loại T và sau đó các nhà xây dựng bản sao nên được gọi. Tại thời điểm này, với một hàm tạo rõ ràng, mã không được biên dịch. Có vẻ như hợp lý để mong đợi một thực hiện, mặt khác, để trực tiếp gọi một nhà xây dựng lấy làm đối số các iterator dereferenced, trong trường hợp các cuộc gọi constructor sẽ được rõ ràng và do đó mã nên biên dịch.Điều này sẽ đi ngược lại từ ngữ chính xác trong báo giá bên dưới, vì hàm tạo bản sao được định nghĩa cho một kiểu T nhất định làm một hàm tạo lấy một tham chiếu duy nhất có thể liên quan đến một đối tượng kiểu T.

Tôi không thể nghĩ bất kỳ lý lẽ hợp lý nào không sử dụng cách tiếp cận Comeau, và tin của tôi (đây chỉ là ý kiến ​​cá nhân) là từ ngữ trong tiêu chuẩn liên quan đến độ phức tạp của constructor vector có thể được phục hồi như yêu cầu chỉ N gọi tới T thích hợp constructor, khi thích hợp sẽ phải được định nghĩa là hàm tạo khớp với lệnh gọi T(*first) (nghĩa là, hoặc là hàm tạo tham chiếu InputIterator::value_type (theo giá trị hoặc có thể tham chiếu không đổi) hoặc bản sao T const ructor sau khi một chuyển đổi ngầm từ InputIterator::value_type để T.

23.2.4.1 [lib.vector.cons]/1

phức tạp: Các nhà xây dựng mẫu vector (InputIterator đầu tiên, InputIterator cuối cùng) làm chỉ N các cuộc gọi đến nhà xây dựng bản sao của T (trong đó N là khoảng cách giữa đầu tiên và cuối cùng) và không có phân bổ lại nếu trình lặp đầu tiên và cuối cùng là các loại truy cập về phía trước, hai chiều hoặc ngẫu nhiên . Nó làm cho các lệnh gọi N tới hàm tạo bản sao của T và nhật ký thứ tự N phân bổ lại nếu chúng là chỉ các trình lặp đầu vào.

Tôi muốn biết làm thế nào trình biên dịch VS cư xử khi đưa ra:

struct T1; 
struct T2 { 
    operator T1(); 
}; 
struct T1 { 
    T1(T2 const &) { std::cout << "T1(T2)" << std::endl; } 
}; 
T2::operator T1() { 
    std::cout << "T2::operator T1" << std::endl; 
    return T1(*this); 
} 
int main() { 
    std::vector<T2> v2; 
    v2.push_back(T2()); 
    std::vector<T1> v1(v2.begin(), v2.end()); 
} 

Với g ++ kết quả là T2::operator T1 không được gọi, mà đúng hơn là các yếu tố trong v1 được xây dựng trực tiếp từ các yếu tố trong v2. Tôi sẽ giả định rằng với VS trình biên dịch sẽ sử dụng T2::operator T1 để chuyển đổi từ mỗi phần tử trong v2 thành phần tử T1 và sau đó gọi hàm tạo bản sao. Là vậy sao?

+0

Trong thực tế, cố gắng biên dịch mã của bạn bằng trình biên dịch VS cho lỗi (C2664): 'std :: allocator <_Ty> :: construct': không thể chuyển đổi tham số 2 từ 'T2' sang 'const T1 &' bằng [_Ty = T1 ] Lý do: không thể chuyển đổi từ 'T2' sang 'const T1' Không có toán tử chuyển đổi do người dùng xác định có thể thực hiện chuyển đổi này hoặc toán tử không thể gọi là – user200783

+0

Tôi vừa thử nghiệm với bản beta VS2010 và không chỉ biên dịch mà còn thực thi với cùng một hành vi mà g ++: phương thức khởi tạo 'T1 (T2 const &)' được gọi. –

1

Điều này thực sự là câu hỏi về cách thư viện STL được triển khai, chứ không phải vấn đề về đặc tả ngôn ngữ. Không có gì trong các đặc tả ngôn ngữ mà sẽ ngăn chặn điều này từ làm việc, cũng không có bất cứ điều gì mà sẽ yêu cầu rằng nó sẽ làm việc.

Nếu stl :: vector constructor được viết để thử chuyển đổi ngầm sử dụng toán tử gán, thì nó sẽ thất bại. Có nhiều khả năng là việc triển khai Microsoft STL sử dụng tối ưu hóa giá trị trả về trong quá trình khởi tạo thông qua một cuộc gọi hàm tạo, trong trường hợp này mã này sẽ hoạt động tốt. Điều quan trọng cần lưu ý là lý do duy nhất hoạt động này là do stl :: vector constructor là templated và yêu cầu duy nhất là nó là một input_iterator, hoặc chính xác hơn là nó hỗ trợ tất cả các chức năng được yêu cầu của một đầu vào trình lặp.

Tôi cũng muốn chỉ ra rằng đây là một ví dụ điển hình về lý do tại sao thường khó viết mã đa nền tảng. Đôi khi bạn kết thúc với các vấn đề mà trình biên dịch không nhất thiết phải lệch khỏi tiêu chuẩn ngôn ngữ, nhưng mã vẫn không phải là di động.

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