在派生类初始化列表中初始化模板基类成员类型

时间:2009-07-13 04:24:37

标签: c++ inheritance templates initialization

以下是一些代码,概述了我一直在努力解决的问题。最后一个问题(就目前而言g ++而言)是:“执行Bar :: Bar(...)构造函数例程时,错误:'Foo-T'未在此范围内声明”。否则,我试图学习的问题是基于使用模板传递给派生类构造函数的参数设置基类成员类型。如果只是通过将参数传递给derived-class构造函数来设置基类成员类型(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;
}

3 个答案:

答案 0 :(得分:15)

在派生(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”(然后,基类也完全已知)

请注意,如果您想使用基类中的函数,您还可以添加using声明..

(在Bar()内部

  some_foo_func(); // wouldn't work either

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

答案 1 :(得分:3)

很抱歉没有帮助,但如果没有完全按照你所说的做法,我也没有办法解决这个问题:

  

到目前为止,我看不到过去使用的方法   模板参数和a   匹配的派生类构造函数   完成这项任务的论据。

您可能需要专注一点:

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;
}

答案 2 :(得分: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编译器仍然会遇到这个问题。