2012-07-18 35 views
12

Tôi đang thực hiện một số C++ quy tắc phân tích tĩnh, và một trong số họ cấm một hàm từ trả về một tham chiếu hoặc con trỏ đến một tham số tham chiếu của chức năng, tức là sau đều không tuân thủ:Quy tắc phân tích tĩnh C++ này có ý nghĩa như thế nào không?

int *f(int& x) { return &x; } // #1 
const int *g(const int& x) { return &x; } // #2 
int& h(int& x) { return x; } // #3 
const int& m(const int& x) { return x; } // #4 

Các biện minh cho điều này là "Đó là hành vi được xác định thực hiện cho dù tham số tham chiếu là đối tượng tạm thời hay tham chiếu đến tham số."

Tôi đang bối rối bởi điều này, tuy nhiên, bởi vì các nhà khai thác luồng trong C++ được viết theo cách này, ví dụ:

std::ostream& operator<<(std::ostream& os, const X& x) { 
    //... 
    return os; 
} 

Tôi nghĩ rằng tôi khá tự tin rằng các nhà khai thác luồng trong C++ không thể hiện hành vi được xác định thực hiện, vậy điều gì đang diễn ra? Theo sự hiểu biết của tôi như hiện tại, tôi mong đợi # 1 và # 3 được xác định rõ ràng, trên cơ sở đó thời gian không thể bị ràng buộc với các tham chiếu không const, do đó, int& x đề cập đến một đối tượng thực sự có tuổi thọ vượt quá phạm vi của hàm, do đó trả về một con trỏ hoặc tham chiếu đến đối tượng đó là tốt. Tôi mong đợi # 2 là tinh ranh, bởi vì một tạm thời có thể đã bị ràng buộc để const int& x, trong trường hợp đó cố gắng để có địa chỉ của nó sẽ có vẻ là một kế hoạch xấu. Tôi không chắc chắn về # 4 - cảm giác ruột của tôi là điều đó cũng có khả năng tinh ranh, nhưng tôi không chắc chắn. Cụ thể, tôi không rõ ràng về những gì sẽ xảy ra trong trường hợp sau:

const int& m(const int& x) { return x; } 
//... 
const int& r = m(23); 
+0

Bạn đang sử dụng MSVC++? – Nawaz

+0

@Nawaz: Tôi đang sử dụng .QL để viết các truy vấn trên các cơ sở mã lớn :) Tôi không nghĩ nó quan trọng đối với trình biên dịch nào, tôi lý tưởng tìm kiếm một câu trả lời độc lập cho nền tảng. –

+2

Tại sao tôi hỏi vì MSVC++ cung cấp phần mở rộng trình biên dịch cho phép các thời gian liên kết với các tham chiếu không phải const. Và nếu bạn đang sử dụng công cụ phân tích tĩnh của Microsoft, thì nó cũng có thể xem xét phần mở rộng này. – Nawaz

Trả lời

8

Như bạn nói, # 1 và # 3 là tốt (mặc dù # 1 được cho là kiểu xấu).

# 4 là tinh ranh vì cùng lý do # 2; nó cho phép truyền một tham chiếu const đến một quá khứ của nó.

Hãy kiểm tra:

#include <iostream> 

struct C { 
    C() { std::cout << "C()\n"; } 
    ~C() { std::cout << "~C()\n"; } 
    C(const C &) { std::cout << "C(const C &)\n"; } 
}; 

const C &foo(const C &c) { return c; } 

int main() { 
    const C &c = foo(C()); 
    std::cout << "c in scope\n"; 
} 

đầu ra này:

C() 
~C() 
c in scope 
+0

Cảm ơn - Tôi không chắc liệu cuộc đời tạm thời có kéo dài bằng cách làm điều này hay không. Có vẻ như câu trả lời là một công ty 'không', ta. –

+0

và những gì về các nhà khai thác dòng? – Arne

+3

@Arne: Chúng lấy và trả về tham chiếu không phải const, vì vậy chúng ổn. –

1

Trong an toàn nếu có cũng quá tải tài liệu tham khảo rvalue 11, # 2 và # 4 có thể được thực hiện C++. Do đó:

const int *get(const int &x) { return &x; } 
const int *get(const int &&x) { return nullptr; } 

void test() { 
    const int x = 0; 
    const int *p1 = get(x); // OK; p1 is &x. 
    const int *p2 = get(x+42); // OK; p2 is nullptr. 
} 

Vì vậy, mặc dù chúng kém chất lượng, chúng có sử dụng an toàn nếu người lập trình biết họ đang làm gì. Nó sẽ là draconian để cấm điều này.

(Có lẽ an toàn hơn nếu quá tải tham chiếu const rvalue được thực hiện riêng tư, không xác định trước hoặc gây ra lỗi biên dịch hoặc thời gian liên kết. Điều này đặc biệt đúng đối với trường hợp số 4, nơi chúng tôi trả về tham chiếu nhưng không có gì tốt để trả về một tham chiếu và ngôn ngữ không cho phép tham chiếu đến null.)

+0

Cảm ơn, điểm tốt, đặc biệt là cho tương lai. Bối cảnh của điều này là tôi đang thực hiện một quy tắc khá draconian cho công cụ thương mại của chúng tôi - chúng được ghim xuống theo một tiêu chuẩn, vì vậy tôi phải cẩn thận về việc lệch quá xa so với chữ của các quy tắc. Điều đó nói rằng, mục đích là để giảm dương tính giả bất cứ nơi nào có thể. Chúng tôi chưa hỗ trợ C++ 11, nhưng khi chúng tôi chắc chắn sẽ có khuynh hướng sửa đổi quy tắc để tính toán quá tải tham chiếu rvalue. –

+0

Một trong những trường hợp trả về tham chiếu đến tham số rvalue là "an toàn" là với 'std :: move' và' std :: forward', cả hai có thể lấy tham số rvalue. IOW, nếu bạn đang lập kế hoạch chuyển kết quả của một hàm như vậy trực tiếp đến một biểu thức khác, bất kể nó có thể là gì (giống như một hàm khác). – Xeo

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