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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1117693/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:52:10  来源:igfitidea点击:

Initializing template base-class member types in derived-class initializer lists

c++inheritancetemplatesinitialization

提问by Shamster

Here is some code outlining a problem I've been wrestling with. The final problem (as far as g++ is concerned at the moment) is that: "error: 'Foo-T' was not declared in this scope" when performing the Bar::Bar(...) constructor routine. Otherwise, the problem I'm attempting to learn my way through is one of setting base-class member types based on arguments passed to a derived-class constructor using templates. If there were a way to set the base-class member type (T Foo-T) simply by passing arguments to the derived-class constructor, I would prefer that. 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. Can you spot anything in the following code that I can be doing better to achieve the same goals? I'm rather new to generic-coding and templates.

这是一些代码,概述了我一直在努力解决的问题。最后一个问题(就目前 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;
}

回答by Pieter

The Foo_T type will not be looked up in the base class when used in the derived (Bar) constructor.

在派生 (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
}

This is per the C++ standard, which says unqualified names are generally non-dependent, and should be looked up when the template is fully defined.

这是根据 C++ 标准,它说非限定名称通常是非依赖的,并且应该在模板完全定义时进行查找。

Since a template base class definition is not known at that time (there could be fully specialised instances of the template being pulled in later in the compilation unit), unqualified names are never resolved to names in dependent base classes.

由于当时不知道模板基类定义(在编译单元后面可能会引入模板的完全专用实例),因此未限定的名称永远不会解析为依赖基类中的名称。

If you need a name from a base class when templates are involved, you have to either fully qualify them, or make them implicitly dependent in your derived class.

如果在涉及模板时需要来自基类的名称,则必须完全限定它们,或者使它们隐式依赖于派生类。

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

or, make it dependent

或者,让它依赖

 this->Foo_T = TypeA(a_arg);

Since the thismakes it template dependent, resolving the type is postponed till "phase 2" of template instantiation (and then, the base class is also fully known)

由于this使其依赖于模板,因此将解析类型推迟到模板实例化的“第 2 阶段”(然后,基类也完全已知)

Note that if you wanted to use a function from the base class, you could have also added a using declaration..

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

(inside Bar())

(在 Bar() 内)

  some_foo_func(); // wouldn't work either

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

回答by Jesse Vogt

Sorry to be unhelpful, but I also do not see a way around this without doing exactly what you stated:

很抱歉没有帮助,但如果不完全按照您所说的去做,我也看不到解决方法:

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.

到目前为止,我看不到同时使用模板参数和匹配的派生类构造函数参数来完成此任务的方法。

You will likely have to specialize a bit:

您可能需要专攻一点:

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

Unfortunately, I do not have access to a compiler at the moment to check this code so be wary.

不幸的是,我目前无法使用编译器来检查这段代码,所以要小心。



In answer to your question/comment. I got the following to compile:

回答您的问题/评论。我得到以下编译:

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

回答by csj

When I first looked at your code, I was absolutely certain that you would have to resolve the problem with partial specializing. Indeed, that may still be the case, however, I have reduced the amount of code necessary to replicate your error, and have observed that the error only occurs when compiling with gcc (I don't know which compiler version my university is running though), and when compiling with Visual Studio 2003 - everythying is happy.

当我第一次查看您的代码时,我绝对确定您必须使用部分专业化来解决问题。确实,情况可能仍然如此,但是,我减少了复制错误所需的代码量,并且观察到错误仅在使用 gcc 编译时发生(尽管我不知道我的大学正在运行哪个编译器版本) ),并且在使用 Visual Studio 2003 进行编译时 - 一切都很愉快。

The following replicates the error code, but a minor, seemingly innocent change will suprisingly eliminate it:

下面复制了错误代码,但一个看似无害的小改动会令人惊讶地消除它:

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

Now, if you remove the template declaration from ClassB, and have it directly inherit from ClassA:

现在,如果您从 ClassB 中删除模板声明,并让它直接从 ClassA 继承:

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

and then change the declaration of cb to match

然后更改 cb 的声明以匹配

    ClassB cb;

Then the error vanishes, even though there is clearly nothing different regarding the scope of vA (or in your case, Foo_T)

然后错误消失,即使 vA(或在您的情况下,Foo_T)的范围显然没有什么不同

I'm speculating that this is a compiler bug, and wondering if perhaps a completely up to date gcc compiler would still experience the issue.

我推测这是一个编译器错误,并且想知道是否可能一个完全最新的 gcc 编译器仍然会遇到这个问题。