2011-07-27 9 views
8

Tôi đã dành nhiều ngày trong một vấn đề lạ và cuối cùng phát hiện ra rằng có hai chức năng inline có cùng chữ ký trong dự án và chúng gây ra sự cố. Để đơn giản hóa tình hình ở đây là một ví dụ: hai cpp file:Điều gì sẽ xảy ra nếu xác định lại chức năng nội dòng?

a.cpp

#include <iostream> 

void b(); 

inline void echo() 
{ 
    std::cout << 0 << std::endl; 
} 

int main() 
{ 
    echo(); 
    b(); 
    return 0; 
} 

và b.cpp

#include <iostream> 

inline void echo() 
{ 
    std::cout << 1 << std::endl; 
} 

void b() 
{ 
    echo(); 
} 

Xin lưu ý rằng inline chức năng echo có chữ ký giống nhau nhưng khác nhau thực hiện. Biên dịch và chạy

g++ a.cpp b.cpp -o a.out && ./a.out 

Hoặc như thế này

g++ a.cpp -c 
g++ b.cpp -c 
g++ a.o b.o -o a.out 
./a.out 

It in 0 0. (Tôi đã sử dụng g ++ 4.6.1 cho rằng, và tôi thử nghiệm với kêu vang ++ 2.9, cùng kết quả)

Điều đó sẽ không xảy ra nếu bật tối ưu hóa, như

g++ -O3 a.cpp b.cpp -o a.out && ./a.out 

Đó là 0 1 thời gian này.

Câu hỏi của tôi là, bất kể kết quả hoặc cách biên dịch thực hiện, không có lỗi hoặc thậm chí cảnh báo về tôi đã xác định các hàm inline nhiều lần. Điều gì trên trái đất xảy ra với trình biên dịch và mối liên kết trong tình huống như thế này?

EDIT:

Hãy nhìn vào những biểu tượng trong đối tượng tập tin

nm a.o b.o | c++filt 

Cả hai file có kỷ lục echo(). Vì vậy, tôi nghĩ rằng vấn đề xảy ra tại thời gian liên kết. Có thể nói rằng trình liên kết ngẫu nhiên chọn một triển khai và loại bỏ tất cả các cài đặt khác?

+0

Bạn đã thử độ dài cảnh báo cao hơn (-Wall, v.v.)? – schnaader

+0

Tôi vừa thử '-Wall -Wextra', vẫn không có cảnh báo. – neuront

Trả lời

5

Trình biên dịch không bắt buộc phải chẩn đoán vi phạm ODR này, và nó không phải là tầm thường. Từ khóa inline có nghĩa là các đơn vị dịch khác nhau có thể có cùng biểu tượng, vì vậy nó được đánh dấu yếu bởi trình biên dịch. Trường hợp sử dụng cơ bản là một hàm được định nghĩa nội tuyến trong một tiêu đề: tất cả các đơn vị dịch bao gồm tiêu đề sẽ có định nghĩa và nó hoàn toàn ổn. Trình biên dịch chỉ cần loại bỏ tất cả trừ một định nghĩa và sử dụng định nghĩa đó ở mọi nơi.

Phát hiện xem các định nghĩa khác nhau có phù hợp chính xác hay không là một vấn đề phức tạp.Trình liên kết sẽ phải phân tích việc triển khai nhị phân được tạo ra và xác định xem hai mã nhị phân có liên quan đến cùng một mã nguồn hay không. Hầu hết các trình biên dịch không có hỗ trợ để xác định điều này.

Tính đến vấn đề cụ thể của bạn, tôi có thể không thể biết lý do dẫn đến việc hai chức năng được đánh dấu nội tuyến, nhưng một lỗi phổ biến được sử dụng từ khóa inline để đại diện cho optimize hơn không phàn nàn lần lặp lại ở thời gian liên kết. Từ khóa inline có ý nghĩa trong tiêu đề, nhưng không quá nhiều trong tệp cpp. Trong tệp cpp, nếu bạn muốn yếu tố một số đoạn mã vào hàm trợ giúp, hàm đó phải được đánh dấu là static hoặc được xác định trong một không gian tên chưa đặt tên là. Nếu hàm này là static thì trình biên dịch biết rằng tất cả các tập quán của hàm đó nằm trong đơn vị dịch của bạn, và nó có kiến ​​thức lớn hơn để quyết định xem nó muốn nội tuyến hay không gọi hàm (lưu ý rằng nó có thể nội tuyến ngay cả khi bạn không 't nói với nó, trong cùng một cách mà nó có thể quyết định không inline ngay cả khi bạn nói với nó).

12

Trong tiêu chuẩn C++ được nêu rõ hơn tất cả các định nghĩa của hàm nội tuyến sẽ giống nhau, nhưng không cần chẩn đoán. Đó là, chương trình của bạn không phải là một chương trình C++ hợp lệ, nhưng việc triển khai có quyền không phát hiện lỗi đó.

Xem khoản 3.2.5. Quá dài để đăng ở đây.

6

Trường hợp này (hai hàm nội tuyến có cùng tên và cùng chữ ký có triển khai khác nhau) leads to undefined behavior. Trình biên dịch là không cần thiết để chẩn đoán nó, mặc dù nó có thể cố gắng.