2016-06-18 12 views
10

Xem xét chương trình sau đây với hai đơn vị biên dịch.Chuỗi trong tiêu đề - điều này có vi phạm ODR không?


// a.hpp 

class A { 
    static const char * get() { return "foo"; } 
}; 

void f(); 

// a.cpp 

#include "a.hpp" 
#include <iostream> 

void f() { 
    std::cout << A::get() << std::endl; 
} 

// main.cpp 

#include "a.hpp" 
#include <iostream> 

void g() { 
    std::cout << A::get() << std::endl; 
} 

int main() { 
    f(); 
    g(); 
} 

Nó là khá phổ biến cần phải tạo ra các hằng chuỗi toàn cầu đối với một số lý do nào khác. Làm điều này theo cách hoàn toàn ngây thơ gây ra các vấn đề liên kết. Thông thường, mọi người đặt một tuyên bố trong tiêu đề và định nghĩa trong một đơn vị biên dịch đơn lẻ hoặc sử dụng macro.

Tôi đã có ấn tượng rằng cách làm này (được hiển thị ở trên) với chức năng là "không sao", bởi vì nó là một hàm inline và trình liên kết loại bỏ bất kỳ bản sao nào được tạo và chương trình được viết bằng mô hình dường như hoạt động tốt. Tuy nhiên, bây giờ tôi có nghi ngờ về việc liệu nó có thực sự hợp pháp hay không.

Chức năng A::get là odr-được sử dụng trong hai đơn vị dịch thuật khác nhau, nhưng nó là hoàn toàn nội tuyến vì nó là một thành viên lớp.

Trong [basic.def.odr.6] nó khẳng định:

Có thể có nhiều hơn một định nghĩa của một ... chức năng inline với liên kết bên ngoài (7.1.2) ... trong một chương trình với điều kiện là mỗi định nghĩa xuất hiện trong một đơn vị dịch thuật khác và cung cấp các định nghĩa đáp ứng các yêu cầu sau đây. Cho một thực thể như vậy là D được xác định trong nhiều đơn vị dịch, sau đó
- 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à
- trong mỗi định nghĩa của D, tên tương ứng, được tra cứu theo 3.4, phải tham chiếu đến một thực thể được xác định trong định nghĩa D hoặc gọi cùng một thực thể sau khi quá tải (13.3) và sau khớ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 không đối xứng 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 một biểu thức không đổi (5.19) và đối tượng không được sử dụng odr 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 sẽ có cùng một liên kết ngôn ngữ; và
- ... (nhiều điều kiện dường như không có liên quan)

Nếu định nghĩa của D đáp ứng tất cả các yêu cầu này, sau đó chương trình sẽ cư xử như thể có một định nghĩa duy nhất của D. Nếu định nghĩa của D không đáp ứng các yêu cầu này, thì hành vi sẽ không được xác định.

Trong chương trình mẫu của tôi, hai định nghĩa (một trong mỗi đơn vị dịch) tương ứng với cùng một chuỗi mã thông báo. (Đây là lý do tại sao tôi ban đầu nghĩ rằng nó là okay.)

Tuy nhiên, không rõ điều kiện thứ hai có được thỏa mãn hay không. Bởi vì, tên "foo" có thể không tương ứng với cùng một đối tượng trong hai đơn vị biên dịch - nó có khả năng là một "chuỗi" khác nhau trong mỗi chữ, phải không?

tôi đã cố gắng thay đổi chương trình:

static const void * get() { return static_cast<const void*>("foo"); } 

để nó in địa chỉ của chuỗi literal, và tôi nhận được cùng một địa chỉ, tuy nhiên tôi không chắc chắn nếu đó là bảo đảm để xảy ra.

Tệp có nằm trong "... phải chỉ đến một thực thể được xác định trong định nghĩa của D" không? Có phải "foo" được coi là được xác định trong vòng A::get tại đây không? Nó có thể có vẻ như vậy, nhưng khi tôi hiểu không chính thức, chuỗi chữ cuối cùng gây ra trình biên dịch phát ra một số loại toàn cầu const char[] mà sống trong một phân đoạn đặc biệt của thực thi. Có phải "thực thể" đó được coi là nằm trong phạm vi A::get hoặc không liên quan?

Có phải "foo" thậm chí được coi là "tên" hay cụm từ "tên" chỉ tham chiếu đến từ định danh C++ "hợp lệ", như có thể được sử dụng cho một biến hoặc hàm? Một mặt nó nói:

[basic][3.4]
Một tên là sử dụng một định danh (2.11), nhà điều hành chức năng-id (13.5), đen-hành-id (13.5.8), conversion- chức năng-id (12.3.2), hoặc mẫu-id (14.2) biểu thị một thực thể hoặc nhãn (6.6.4, 6.1).

và một định danh là

[lex.name][2.11]
Một định danh là một chuỗi tùy tiện lâu các chữ cái và chữ số.

vì vậy có vẻ như một chuỗi chữ không phải là tên.

Mặt khác trong phần 5

[expr.prim.general][5.1.1.1]
Một chuỗi literal là một giá trị trái; tất cả các số khác là là giá trị.

Nói chung, tôi nghĩ rằng lvalues có tên.

+2

Vì thẻ này được gắn thẻ [ngôn ngữ-luật sư], tôi sẽ để nó được trả lời bởi người sẽ theo dõi các trích dẫn cụ thể có liên quan; nhưng FWIW, "tên" có nghĩa là về cơ bản "một thứ có thể được khai báo" - như, tên biến, tên hàm, tên lớp, v.v. - và các chuỗi ký tự là chắc chắn * không * tên. (Xem http://en.cppreference.com/w/cpp/language/identifiers#Names để liệt kê chính thức hơn một chút.) – ruakh

+0

Đó là một câu hỏi học thuật. Vì vậy, nó rất quan tâm đến những người đang tạo ra tiêu chuẩn mới, những người dường như không thể nghĩ ra giải pháp đơn giản là cắm lỗ nếu có một (tôi nghĩ rằng quá trình đó bị ô nhiễm ở đâu đó giữa C++ 11 và C++ 14). Trong thực tế, một hàm 'inline' tạo ra một bản ghi liên kết có thể hủy bỏ, và trình liên kết chỉ chọn một định nghĩa duy nhất, trong thực tế, đảm bảo tính duy nhất của chuỗi - ít nhất là nếu tối ưu hóa mã máy tính tôn trọng hành vi đó. –

+0

Nếu bạn muốn tìm kiếm các cân nhắc, như tôi nhớ lại lần cuối tôi thấy điều này đã được thảo luận trong một đề xuất về dữ liệu 'nội tuyến '(nơi chúng không xem xét việc cắm lỗ). –

Trả lời

7

Đối số cuối cùng của bạn là vô nghĩa. "foo" thậm chí không phải là tên theo ngữ pháp, mà là chuỗi chữ. Và các chuỗi ký tự là các giá trị và một số giá trị có tên không hàm ý rằng các chuỗi ký tự có hoặc có tên. Các chuỗi ký tự như được sử dụng trong mã của bạn không vi phạm ODR. Thực ra, cho đến khi C++ 11, bắt buộc các chuỗi ký tự trong nhiều định nghĩa của các hàm nội tuyến trên các TU chỉ định cùng một thực thể, nhưng quy tắc thừa và hầu hết không được thực hiện đã bị xóa bởi CWG 1823.

Bởi vì, tên "foo" có thể không tương ứng với cùng một đối tượng trong các đơn vị hai biên soạn - đó là khả năng một "khác biệt" chuỗi chữ trong mỗi, không có?

Đúng, nhưng điều đó không liên quan. Bởi vì ODR không quan tâm đến các giá trị đối số cụ thể. Nếu bạn đã quản lý bằng cách nào đó có được một ví dụ khác chức năng chuyên môn mẫu được gọi trong cả hai TUs, đó sẽ là vấn đề, nhưng may mắn chuỗi chữ là đối số mẫu không hợp lệ, vì vậy bạn sẽ phải thông minh.

+0

Tôi đoán tính năng nổi bật nhất là tên, là độ phân giải tên sẽ xảy ra. Rõ ràng, không gian tên, ẩn, và như vậy, không áp dụng cho '" foo "', do đó, nên có được một gợi ý lớn với tôi rằng nó không phải là một tên. –

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