2009-07-13 104 views
11

下面是一些代碼,概述了我一直在摔跤的問題。最後的問題(就目前而言,g ++而言)是:「執行Bar :: Bar(...)構造函數例程時,錯誤:'Foo-T'未在此範圍內聲明。否則,我試圖學習我的方法的問題是基於使用模板傳遞給派生類構造函數的參數來設置基類成員類型。如果有辦法簡單地通過將參數傳遞給派生類構造函數來設置基類成員類型(T Foo-T),我寧願這樣做。到目前爲止,我看不到使用模板參數和匹配的派生類構造函數參數來完成此任務的方式。你能否在下面的代碼中發現任何事情,我可以更好地實現相同的目標?我對通用編碼和模板比較陌生。在派生類初始化列表中初始化模板基類成員類型

#include <iostream> 
typedef int a_arg_t; 
typedef double b_arg_t; 
typedef std::string foo_arg_t; 

class TypeA { 
public: 
    TypeA(); 
    TypeA (a_arg_t a) { 
     /* Do sosmething with the parameter passed in */ 
    } 

}; 

class TypeB { 
public: 
    TypeB(); 
    TypeB (b_arg_t b) { 
     /* typeB's constructor - do something here */ 
    } 

}; 

// The base-class with a member-type to be determined by the template argument 
template <class T> 
class Foo { 

public: 
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)  // initialize something here 
    { 
     /* do something for foo */ 
    } 
    T Foo_T;  // either a TypeA or a TypeB - TBD 
    foo_arg_t _foo_arg; 
}; 

// the derived class that should set the basse-member type (T Foo_T) 
template <class T> 
class Bar : public Foo<T> { 
public: 
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
    : Foo<T>(bar_arg) // base-class initializer 
    { 
     // the initialization of Foo_T has to be done outside the initializer list because it's not in scsope until here 
     Foo_T = TypeA(a_arg); // if an a_arg_t is passed in, then we set the Foo_T to TypeA, etc. 
    } 

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg) 
    : Foo<T>(bar_arg) 
    { 
     Foo_T = TypeB(b_arg); 
    } 

}; 

int main() { 

    b_arg_t b_arg; 
    a_arg_t a_arg; 
    foo_arg_t bar_arg; 

    Bar<TypeA> a (bar_arg, a_arg); // try creating the derived class using TypeA 
    Bar<TypeB> b (bar_arg, b_arg); // and another type for show 

return 0; 
} 
+0

注意:TypeA和TypeB上的默認構造函數需要body,否則會得到鏈接錯誤。 – csj 2009-07-13 07:05:32

回答

14

在派生(Bar)構造函數中使用時,不會在基類中查找Foo_T類型。

Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
: Foo<T>(bar_arg) // base-class initializer 
{ 
    Foo_T = TypeA(a_arg); TypeA, etc. // Won't compile, per the standard 
} 

這是每個C++標準,它說不合格的名稱通常是不相關的,並且應該擡起頭來時,模板是完全定義。由於模板基類定義在那個時候是未知的(在編譯單元中後面可能有完全特定的模板實例),所以非限定名稱永遠不會解析爲依賴基類中的名稱。

如果在涉及模板時需要基類的名稱,則必須完全限定它們,或使它們隱式地依賴於派生類。

Foo<T>::Foo_T = TypeA(a_arg); // fully qualified will compile 

,或者使其依賴

this->Foo_T = TypeA(a_arg); 

由於this使得模板依賴性的,解決的類型被推遲到模板實例的「階段2」(然後,基類也完全知)

需要注意的是,如果你想用一個函數從基類,你可以有還增加了使用聲明..

(內部Bar())

some_foo_func(); // wouldn't work either 

    using Foo<T>::some_foo_func; 
    some_foo_func(); // would work however 
3

對不起,是無益的,但我還沒有看到解決的辦法,也不一定是做你說什麼:

As of now I can't see a way past using both the template argument and a matching derived-class constructor argument to accomplish this task.

你可能有專攻一點:

template<> 
Bar<TypeA>::Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
: Foo<TypeA>(bar_arg) // base-class initializer 
{ 
    // the initialization of Foo_T has to be done outside the initializer list because it's not in scsope until here 
    Foo_T = TypeA(a_arg); // if an a_arg_t is passed in, then we set the Foo_T to TypeA, etc. 
} 

template< class T> 
Bar<T>::Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
: Foo<T>(bar_arg) // base-class initializer 
{ 
    // Throw exception? 
} 

template<> 
Bar<TypeB>::Bar (const foo_arg_t bar_arg, const b_arg_t b_arg) 
: Foo<TypeB>(bar_arg) 
{ 
    Foo_T = TypeB(b_arg); 
} 

template< class T > 
Bar<T>::Bar (const foo_arg_t bar_arg, const b_arg_t b_arg) 
: Foo<T>(bar_arg) 
{ 
    // Throw exception ? 
} 

不幸的是,我目前無法訪問編譯器來檢查此代碼,因此請謹慎。


在回答您的問題/評論。我得到以下編譯:

#include <iostream> 
typedef int a_arg_t; 
typedef double b_arg_t; 
typedef std::string foo_arg_t; 

class TypeA { 
public: 
    TypeA() {} 
    TypeA (a_arg_t a) {} 
}; 

class TypeB { 
public: 
    TypeB() {} 
    TypeB (b_arg_t b) {} 
}; 

template <class T> 
class Foo { 
public: 
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg) {} 
    T Foo_T;  // either a TypeA or a TypeB - TBD 
    foo_arg_t _foo_arg; 
}; 

// the derived class that should set the basse-member type (T Foo_T) 
template <class T> 
class Bar : public Foo<T> { 
public: 
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
    : Foo<T>(bar_arg) // base-class initializer 
    { 
    Foo<T>::Foo_T = TypeA(a_arg); 
    } 

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg) 
    : Foo<T>(bar_arg) 
    { 
    Foo<T>::Foo_T = TypeB(b_arg); 
    } 
}; 

int main() { 
    b_arg_t b_arg; 
    a_arg_t a_arg; 
    foo_arg_t bar_arg; 

    Bar<TypeA> a (bar_arg, a_arg); // try creating the derived class using TypeA 
    Bar<TypeB> b (bar_arg, b_arg); // and another type for show 

    return 0; 
} 
+0

有關編譯器錯誤的任何想法,說明Foo_T超出了Bar :: Bar(...)構造函數的作用域?派生類的構造函數是否可以訪問基類的公共或受保護成員?我意識到在初始化程序列表中不能訪問基類數據成員,但我始終覺得派生類構造函數可以訪問...... – Shamster 2009-07-13 05:12:13

+0

編輯我的答案以獲取代碼以進行編譯。說實話...我不知道爲什麼我需要明確地說Foo :: Foo_T。 – 2009-07-13 05:31:05

0

當我第一次看你的代碼時,我絕對相信你必須解決部分專業化的問題。事實上,情況可能仍然如此,但是,我已經減少了複製錯誤所需的代碼量,並且觀察到錯誤只發生在使用gcc編譯時發生(我不知道我的大學正在運行哪個編譯器版本),並在使用Visual Studio 2003進行編譯時 - 每個人都很開心。

以下複製錯誤代碼,但是未成年人,貌似無辜的變化會驚奇地消除它:

template <typename T> 
class ClassA 
{ 
public: 
    ClassA() {} 
    T vA; 
}; 


template<typename T> 
class ClassB : public ClassA<T> 
{ 
public: 
    ClassB() 
    { 
     vA = 6; 
    } 
}; 

int main() 
{ 
    ClassB<int> cb; 
} 

現在,如果您從ClassB的模板聲明,並直接從ClassA的繼承:

class ClassB : public ClassA<int> 
{ 
public: 
    ClassB() 
    { 
     vA = 6; 
    } 
}; 

,然後更改CB的聲明相匹配

ClassB cb; 

然後錯誤消失了,儘管關於vA的範圍(或者在你的情況下,Foo_T)顯然沒有任何不同(

我在猜測這是一個編譯器錯誤,並且想知道是否可能是完全最新的gcc編譯器仍然會遇到問題。