2012-02-20 8 views
6

Hãy xem xét ví dụ sau:C++: lớp khác nhau có cùng tên trong các đơn vị dịch khác nhau

// usedclass1.hpp 
#include <iostream> 
class UsedClass 
{ 
public: 
    UsedClass() { } 
    void doit() { std::cout << "UsedClass 1 (" << this << ") doit hit" << std::endl; } 
}; 

// usedclass2.hpp 
#include <iostream> 
class UsedClass 
{ 
public: 
    UsedClass() { } 
    void doit() { std::cout << "UsedClass 2 (" << this << ") doit hit" << std::endl; } 
}; 

// object.hpp 
class Object 
{ 
public: 
    Object(); 
}; 

// object.cpp 
#include "object.hpp" 
#include "usedclass2.hpp" 
Object::Object() 
{ 
    UsedClass b; 
    b.doit(); 
} 

// main.cpp 
#include "usedclass1.hpp" 
#include "object.hpp" 
int main() 
{ 
    Object obj; 
    UsedClass a; 
    a.doit(); 
} 

mã biên dịch mà không cần bất kỳ trình biên dịch hoặc mối liên kết lỗi. Nhưng sản lượng là xa lạ đối với tôi:

  • gcc (Red Hat 4.6.1-9) trên Fedora x86_64 không có tối ưu hóa [EG1]:

    UsedClass 1 (0x7fff0be4a6ff) số tiền rất ít hit
    UsedClass 1 (0x7fff0be4a72e) số tiền rất ít nhấn

  • giống như [EG1] nhưng với -O2 tùy chọn kích hoạt [EG2]:

    UsedClass 2 (0x7fffcef79fcf) số tiền rất ít nhấn
    UsedClass 1 (0x7fffcef79fff) số tiền rất ít nhấn

  • msvc2005 (14.00.50727.762) trên Windows XP 32bit không có tối ưu hóa [EG3] :

    UsedClass 1 (0012FF5B) số tiền rất ít nhấn
    UsedClass 1 (0012FF67) số tiền rất ít nhấn

  • giống như [EG3] nhưng với/O2 (hoặc/Sửu) được kích hoạt [EG4]:

    UsedClass 1 (0012FF73) số tiền rất ít nhấn
    UsedClass 1 (0012FF7F) số tiền rất ít nhấn

tôi mong chờ hoặc là một lỗi mối liên kết (giả sử quy tắc ODR bị vi phạm) hoặc đầu ra như trong [EG2] (mã được inlined, không có gì được xuất khẩu từ translati trên đơn vị, quy tắc ODR được giữ). Vì vậy, câu hỏi của tôi:

  1. Tại sao các đầu ra [EG1], [EG3], [EG4] có thể?
  2. Tại sao tôi nhận được các kết quả khác nhau từ các trình biên dịch khác nhau hoặc thậm chí từ cùng một trình biên dịch? Điều đó làm cho tôi nghĩ rằng tiêu chuẩn bằng cách nào đó không xác định hành vi trong trường hợp này.

Cảm ơn bạn đã đề xuất, nhận xét và diễn giải tiêu chuẩn.

Cập nhật
Tôi muốn hiểu hành vi của trình biên dịch. Chính xác hơn, tại sao không có lỗi nào được tạo ra nếu ODR bị vi phạm. Giả thuyết là vì tất cả các hàm trong các lớp UsedClass1UsedClass2 được đánh dấu là nội tuyến (và do đó C++ 03 3.2 là không vi phạm) trình liên kết không báo cáo lỗi, nhưng trong trường hợp này kết quả đầu ra [EG1], [EG3], [EG4] có vẻ lạ.

+7

ODR bị vi phạm và bạn không được đảm bảo lỗi liên kết khi điều đó xảy ra. Hành vi không xác định. –

+0

Tôi không thể trả lời bạn câu hỏi liên quan đến các tiêu chuẩn nhưng lý do tại sao chúng tôi có không gian tên. – 111111

Trả lời

7

Chương trình của bạn vi phạm Quy tắc một định nghĩa và gọi hành vi không xác định.
Tiêu chuẩn không quy định một thông báo chẩn đoán nếu bạn vi phạm ODR nhưng hành vi là Không xác định.

C++ 03 3.2 Một quy tắc định nghĩa

Không đơn vị dịch thuật phải có nhiều hơn một định nghĩa của bất kỳ biến, chức năng, kiểu lớp, kiểu enumeration hoặc mẫu. ...

Mỗi chương trình phải chứa chính xác một định nghĩa về mọi hàm hoặc đối tượng không phải nội tuyến được sử dụng trong chương trình đó; không cần chẩn đoán. Định nghĩa có thể xuất hiện rõ ràng trong chương trình, nó có thể được tìm thấy trong thư viện chuẩn hoặc thư viện do người dùng định nghĩa, hoặc (khi thích hợp), nó được định nghĩa ngầm định (xem 12.1, 12.4 và 12.8). Một hàm nội tuyến sẽ được định nghĩa trong mọi đơn vị dịch mà nó được sử dụng.

Hơn nữa tiêu chuẩn xác định các yêu cầu cụ thể cho sự tồn tại của nhiều định nghĩa của một biểu tượng, được xác định rõ ràng trong Para # 5 của 3.2.

Có thể có nhiều định nghĩa loại lớp (điều 9), kiểu liệt kê (7.2), hàm nội tuyến có liên kết ngoài (7.1.2), mẫu lớp (điều 14), hàm không tĩnh mẫu (14.5.5), thành phần dữ liệu tĩnh của một mẫu lớp (14.5.1.3), hàm thành viên của một mẫu lớp (14.5.1.1), hoặc chuyên môn mẫu mà một số tham số mẫu không được chỉ định (14.7, 14.5.4) trong một chương trình miễn là mỗi định nghĩa xuất hiện trong một đơn vị dịch thuật khác nhau và cung cấp các định nghĩa thỏa mãn các yêu cầu sau. Với một thực thể tên D được xác định trong nhiều đơn vị dịch, thì

- mỗi định nghĩa của D phải bao gồm cùng một chuỗi mã thông báo; và ...

+0

Bạn đang thiếu quy tắc áp dụng, đây sẽ là quy tắc liên quan đến các định nghĩa không nhất quán của một hàm nội tuyến. –

+1

Phần cụ thể của/5 có liên quan ở đây, là "mỗi định nghĩa của D sẽ bao gồm cùng một chuỗi mã thông báo". Hai định nghĩa của 'UsedClass' khác nhau bởi 1 mã thông báo (một chuỗi ký tự), là đủ. –

+0

'Một hàm nội tuyến sẽ được định nghĩa trong mọi đơn vị dịch mà nó được sử dụng.' Tôi chỉ sử dụng các hàm nội tuyến trong ví dụ, được định nghĩa trong mọi đơn vị dịch. – user1221434

4

Tại sao các kết quả đầu ra [EG1], [EG3], [EG4] có thể?

Câu trả lời đơn giản là hành vi không xác định, vì vậy mọi thứ đều có thể.

Hầu hết các trình biên dịch xử lý một hàm nội tuyến bằng cách tạo bản sao trong mỗi đơn vị dịch mà nó được xác định; trình liên kết sau đó tùy ý chọn một để đưa vào chương trình cuối cùng. Đây là lý do tại sao, với tối ưu hóa bị vô hiệu hóa, nó gọi chức năng tương tự trong cả hai trường hợp. Với tối ưu hóa được kích hoạt, chức năng có thể được biên dịch bởi trình biên dịch, trong trường hợp đó, mỗi cuộc gọi nội tuyến sẽ sử dụng phiên bản được xác định trong đơn vị dịch hiện tại.

Điều đó làm cho tôi nghĩ rằng tiêu chuẩn bằng cách nào đó không chỉ định hành vi trong trường hợp này.

Đúng vậy. Phá vỡ một quy tắc định nghĩa cho hành vi không xác định và không cần chẩn đoán.

+0

Âm thanh khá hợp lý. Ví dụ, '__attribute__ ((always_inline))' trong gcc làm cho trường hợp đầu ra EG1 giống với EG2. Cảm ơn bạn đã xác nhận dự đoán của tôi. – user1221434

12

Đây là quy tắc cấm những gì bạn đang làm (từ ngữ C++ 11), từ phần 3.2 của tiêu chuẩn:

Có thể có nhiều hơn một định nghĩa của một kiểu lớp (khoản 9), kiểu liệt kê (7.2), chức năng inline với mối liên hệ bên ngoài (7.1.2), lớp mẫu (Điều 14), mẫu chức năng không tĩnh (14.5.6), thành phần dữ liệu tĩnh của một mẫu lớp (14.5.1.3), hàm thành viên của một mẫu lớp (14.5.1.1), hoặc chuyên môn khuôn mẫu mà một số tham số mẫu là không được chỉ định (14.7, 14.5.5) trong một chương trình miễn là mỗi định nghĩa xuất hiện trong một đơn vị dịch thuật khác nhau và cung cấp các định nghĩa đáp ứng các yêu cầu sau đây. Với một thực thể như vậy được đặt tên D quy định tại nhiều hơn một đơn vị dịch thuật, sau đó

  • mỗi định nghĩa của D sẽ bao gồm cùng một chuỗi các thẻ; và

  • trong mỗi định nghĩa của D, tên tương ứng, ngẩng đầu lên theo 3.4, sẽ đề cập đến một thực thể được xác định trong định nghĩa của D, hoặc sẽ đề cập đến cùng một thực thể, sau khi giải quyết tình trạng quá tải (13,3) và sau phù hợp của chuyên môn mẫu từng phần (14.8.3), ngoại trừ tên có thể tham chiếu đến đối tượng const với nội bộ hoặc không có liên kết nếu đối tượng có cùng một loại chữ trong tất cả các định nghĩa của D và đối tượng được khởi tạo với cụm từ không đổi (5.19)), và giá trị (nhưng không phải là địa chỉ) của đối tượng được sử dụng, và đối tượng có cùng giá trị trong tất cả các định nghĩa của D; và

  • trong mỗi định nghĩa của D, các thực thể tương ứng phải có cùng liên kết ngôn ngữ; và

  • trong mỗi định nghĩa của D, các toán tử quá tải được gọi, hàm gọi hàm ẩn, hàm tạo, hàm mới và hàm xóa của nhà điều hành, phải tham chiếu cùng chức năng hoặc đến hàm được xác định trong phần tử của D; và

  • trong mỗi định nghĩa của D, đối số mặc định được sử dụng bởi một cuộc gọi hàm (ẩn hoặc rõ ràng) được coi như trình tự mã thông báo có mặt trong trường hợp D; đó là, đối số mặc định phải tuân theo ba yêu cầu được mô tả ở trên (và, nếu đối số mặc định có biểu thức con với các đối số mặc định, yêu cầu này áp dụng đệ quy).

  • nếu D là một lớp có hàm khai báo ngầm (12.1), như thể hàm tạo được định nghĩa ngầm định trong mọi đơn vị dịch mà không được sử dụng và định nghĩa ẩn trong mọi đơn vị dịch sẽ gọi cùng một hàm tạo cho một lớp cơ sở hoặc một thành viên của lớp là D.

Trong chương trình của bạn, bạn đang vi phạm ODR cho class UsedClass vì các thẻ khác nhau trong các đơn vị biên dịch khác nhau. Bạn có thể khắc phục điều đó bằng cách di chuyển định nghĩa của UsedClass::doit() bên ngoài nội dung lớp, nhưng cùng một quy tắc áp dụng cho nội dung của các hàm nội dòng.