2015-09-18 19 views
5

Tôi có một tập tin tiêu đề mà tuyên bố một mẫu với một biến tĩnh và cũng định nghĩa nó:Tôi có được đảm bảo không bị vi phạm ODR này cắn không?

/* my_header.hpp */ 
#ifndef MY_HEADER_HPP_ 
#define MY_HEADER_HPP_ 

#include <cstdio> 

template<int n> 
struct foo { 
    static int bar; 

    static void dump() { printf("%d\n", bar); } 
}; 

template<int n> 
int foo<n>::bar; 

#endif // MY_HEADER_HPP_ 

Tiêu đề này được bao gồm cả bởi main.cpp và thư viện mylib chia sẻ. Cụ thể, mylib_baz.hpp chỉ bao gồm mẫu này và tuyên bố một hàm làm thay đổi chuyên môn của mẫu.

/* mylib_baz.hpp */ 
#ifndef MYLIB_BAZ_HPP_ 
#define MYLIB_BAZ_HPP_ 

#include "my_header.hpp" 

typedef foo<123> mylib_foo; 
void init_mylib_foo(); 

#endif // MYLIB_BAZ_HPP_ 

/* mylib_baz.cpp */ 
#include "mylib_baz.hpp" 
void init_mylib_foo() { 
    mylib_foo::bar = 123; 
    mylib_foo::dump(); 
}; 

Khi tôi làm mylib.so (chứa mylib_baz.o), biểu tượng cho foo<123>::bar có mặt và tuyên bố yếu. Tuy nhiên, biểu tượng cho foo<123>::bar được khai báo yếu còn ở main.o tôi:

/* main.cpp */ 
#include "my_header.hpp" 
#include "mylib_baz.hpp" 

int main() { 
    foo<123>::bar = 456; 
    foo<123>::dump(); /* outputs 456 */ 
    init_mylib_foo(); /* outputs 123 */ 
    foo<123>::dump(); /* outputs 123 -- is this guaranteed? */ 
} 

Dường như tôi đang vi phạm một quy tắc định nghĩa (foo<123>::bar định nghĩa cả về my_header.cppmain.cpp). Tuy nhiên, cả hai với g + + và clang các biểu tượng được tuyên bố yếu (hoặc duy nhất), vì vậy tôi không bị cắn bởi điều này - tất cả các truy cập để foo<123>::bar sửa đổi cùng một đối tượng.

Câu hỏi 1: Đây có phải là sự trùng hợp ngẫu nhiên (có thể ODR hoạt động khác với thành viên tĩnh của mẫu?) Hay tôi thực tế đã đảm bảo hành vi này theo tiêu chuẩn?

Câu hỏi 2: Làm cách nào tôi có thể dự đoán hành vi mà tôi đang quan sát? Đó là, chính xác những gì làm cho trình biên dịch tuyên bố biểu tượng yếu?

+1

Tôi nghĩ tiêu chuẩn nói "Không nên hoạt động" nhưng người liên kết của bạn nói "Không sao". Vi phạm ODR là một ý tưởng tồi - mã sẽ không hoạt động ở mọi nơi. Trong C, có một 'mở rộng phổ biến' cho phép nhiều định nghĩa hoạt động (xem [Làm cách nào để sử dụng 'extern' để chia sẻ các biến giữa các tệp nguồn trong C?] (http://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files-in-c/) - tính năng này thực sự là tài sản của linker, và có cơ hội tốt cho trình biên dịch C và C++ của bạn chia sẻ công nghệ liên kết.) –

+0

Đồng ý! Tôi cũng khá chắc chắn điều này không nên làm việc, nhưng tôi không thể thực sự đặt ngón tay của tôi vào nó tại sao. Trong trường hợp của tôi, tôi có thể (và nên!) Đã khai báo 'bar'' extern' và chỉ định nghĩa nó trong 'mylib_baz.cpp', giải quyết vấn đề này. Nhưng tôi thực sự tò mò muốn đào sâu hơn và xem lý do đằng sau hành vi mà tôi đang quan sát. – FreenodeForsakeMe

+0

"' foo <123> :: thanh' được định nghĩa cả trong my_header.cpp và main.cpp "... Cái gì? Tôi thấy nó được xác định ở một nơi mà không phải của những người bạn đã đề cập: 'my_header.hpp'. Đó là một định nghĩa. – Barry

Trả lời

1

Không có vi phạm ODR. Bạn có một định nghĩa của bar. Nó là ở đây:

template<int n> 
int foo<n>::bar; // <== 

Như barstatic, chỉ ra rằng có một nét trên tất cả các đơn vị dịch. Mặc dù bar sẽ hiển thị một lần trong tất cả các tệp đối tượng của bạn (chúng cần một biểu tượng cho nó, sau khi tất cả), trình liên kết sẽ hợp nhất chúng lại với nhau thành một định nghĩa thực sự của bar. Bạn có thể thấy rằng:

$ g++ -std=c++11 -c mylib_baz.cpp -o mylib_baz.o 
$ g++ -std=c++11 -c main.cpp -o main.o 
$ g++ main.o mylib_baz.o -o a.out 

Tạo:

$ nm mylib_baz.o | c++filt | grep bar 
0000000000000000 u foo<123>::bar 
$ nm main.o | c++filt | grep bar 
0000000000000000 u foo<123>::bar 
$ nm a.out | c++filt | grep bar 
0000000000601038 u foo<123>::bar 

đâu u chỉ:

"u"
Biểu tượng là một biểu tượng toàn cầu độc đáo. Đây là một phần mở rộng của GNU cho tập hợp các ràng buộc biểu tượng ELF tiêu chuẩn. Đối với biểu tượng như vậy, liên kết động sẽ đảm bảo rằng trong toàn bộ quá trình chỉ có một biểu tượng với tên này và loại được sử dụng.

+0

'tĩnh' không phải là những gì có trách nhiệm cho điều này, chính xác. Đó là 'thanh' là một thành viên của một mẫu và có thể được khởi tạo (và thống nhất giữa các đơn vị dịch) cùng với mẫu đó. –

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