2009-03-03 10 views
26

Xét đoạn mã sau:Truy cập biến kế thừa từ lớp cha templated

template<class T> class Foo 
{ 
public: 
    Foo() { a = 1; } 

protected: 
    int a; 
}; 

template<class T> class Bar : public Foo<T> 
{ 
public: 
    Bar() { b = 4; }; 

    int Perna(int u); 

protected: 
    int b; 

}; 

template<class T> int Bar<T>::Perna(int u) 
{ 
    int c = Foo<T>::a * 4; // This works 
    return (a + b) * u; // This doesn't 
} 

g ++ 3.4.6, 4.3.2 và 4.1.2 cho lỗi

test.cpp: In member function `int Bar<T>::Perna(int)': 
test.cpp:25: error: `a' was not declared in this scope 

g ++ 2,96 và MSVC 6, 7 , 7.1, 8 và 9 chấp nhận nó, cũng như (ít nhất) các trình biên dịch Intel và SGI C++ cũ hơn.

Trình biên dịch Gnu C++ mới có tuân thủ tiêu chuẩn hay không? Nếu có, lý do cơ bản đằng sau lớp thừa kế đó không thể thấy biến thành viên được kế thừa được bảo vệ là gì?

Ngoài ra, nếu có

int A() { return a; } 

trong Foo, tôi nhận được lỗi

test.cpp:25: error: there are no arguments to A that depend on a template parameter, so a declaration of A must be available 
test.cpp:25: error: (if you use -fpermissiveâ, G++ will accept your code, but allowing the use of an undeclared name is deprecated) 

khi tôi cố gắng sử dụng nó trong một hàm thành viên của Bar. Tôi thấy rằng tò mò là tốt: Bar thừa hưởng Foo, vì vậy tôi nghĩ rằng đó là rõ ràng rằng A() trong phạm vi của Bar là Foo :: A().

Trả lời

37

Phiên bản GCC sau này thực hiện đúng tiêu chuẩn.

Tiêu chuẩn chỉ định rằng các tên không đủ tiêu chuẩn trong mẫu không phụ thuộc và phải được tra cứu khi mẫu được xác định. Định nghĩa của một lớp cơ sở phụ thuộc không xác định tại thời điểm đó (các chuyên ngành của mẫu lớp cơ sở có thể tồn tại) vì vậy các tên không đủ tiêu chuẩn không thể được giải quyết.

Điều này đúng cho cả tên biến và hàm được khai báo trong lớp cơ sở.

Như bạn đã quan sát giải pháp là cung cấp tên đủ điều kiện của biến hoặc chức năng hoặc để cung cấp tuyên bố "sử dụng". Ví dụ.

template<class T> 
int Bar<T>::Perna(int u) 
{ 
    int c = Foo<T>::a * 4; // This works 
    c = this->a * 4; // and this 

    using Foo<T>::a; 
    c = a * 4; // and with 'using', so should this 
} 

(Tôi thực sự không chắc chắn 100% về cú pháp chính xác cho phiên bản sử dụng và không thể thử nghiệm ở đây nhưng bạn có ý tưởng).

+0

Cảm ơn. Tôi sẽ cuộn tay áo và thay đổi mã. –

+1

Tôi tin rằng bạn phải thực hiện tuyên bố sử dụng ở cấp lớp, ví dụ: mẫu Thanh: công khai Foo {using Foo :: a; ...}; –

4

Thông báo lỗi GCC cho thấy rằng phiên bản GCC của bạn vẫn có lỗi chỉ được giải quyết trong phiên bản GCC4.7 trunk. Các phiên bản cũ, bao gồm GCC4.1 vui vẻ sẽ chấp nhận mã sau

template<typename T> 
struct A { 
    void f(int) { } 
}; 

template<typename T> 
struct B : A<T> { 
    void g() { T t = 0; f(t); } 
}; 

int main() { 
    B<int> b; b.g(); 
} 

GCC sẽ nhìn lên f trong f(t) trong lớp cơ sở A<T> và sẽ tìm ra lời tuyên bố trong lớp cơ sở. GCC thực hiện điều đó bởi vì f phụ thuộc, bởi vì có các đối số cho f rằng "phụ thuộc vào thông số mẫu" (xem thông báo lỗi của nó đã cung cấp cho bạn!). Nhưng Standard cấm GCC để làm điều đó vì hai lý do

  1. Tiêu chuẩn nói rằng việc sử dụng tên không đủ tiêu chuẩn sẽ không bao giờ tìm thấy một bản tuyên bố trong một lớp cơ sở phụ thuộc bất kể tên phụ thuộc.

  2. Tiêu chuẩn cho biết việc tra cứu phụ thuộc tên hàm tại thời điểm khởi tạo sẽ chỉ thực hiện ADL.

GCC 4.7 triển khai chuẩn một cách chính xác trong lĩnh vực đó.