【问题标题】:Wrong definition of function in template inherited class模板继承类中的函数定义错误
【发布时间】:2015-09-17 10:29:38
【问题描述】:

我无法弄清楚模板类和继承的问题。 这是使用的类。

一个带有 typedef 的接口模板类:

template < const unsigned long Number >
class IA
{
protected:
    typedef  TemplateVector<Item*, Number > List; //template array

public:
    virtual List* getList() = 0;
};

具有相同typedef(.hpp)的真实类:

template < unsigned long Number >
class A : public IA< Number >
{
typedef  typename IA< Number >::List List;

public:
    virtual List* getList();

private:
    List* m_list;

};
#include "A.cpp"

具有相同typedef(.cpp)的真实类:

template < unsigned long Number >
TemplateVector<Item*, Number >* A< Number >::getList()
{
    return m_list;
}

问题在于getList() 函数的.cpp 定义。 它在编译时会导致错误,我不知道为什么。错误是

declaration is incompatible with "A<Number>::List *A<Number>::getList()

如果我改变:

typedef  typename IA< Number >::List List

typedef  TemplateVector<Item*, Number > List

它工作正常,但它是一个重新定义,我想避免这种警告。 (请注意,我需要分成 .hpp 和 .cpp 文件,并使用完整性编译器)

【问题讨论】:

  • 解决此问题的另一种方法是将函数定义 (.cpp) 中的标头更改为 typename A&lt;Number&gt;::List* A&lt; Number &gt;::getList(): coliru.stacked-crooked.com/a/79fbe3febd01232b 。事实上,这似乎是最明智的做法。
  • 就这么简单!感谢您的帮助 :) 我之前尝试过没有“typename”的 A::List* A::getList() 。我是如此接近:)
  • typename 部分是 gcc 向我建议的,我自己也总是忘记它。但是,这并不能解决您发布的问题;我希望有更多知识的人能解释为什么您的原始代码无法编译。
  • 旁注:您在IA 中声明您的类型名称为protected,在A 中声明private,除非您公开公开返回这些类型对象的函数。除非用户使用编译时类型推断,否则他们不能使用显式类型变量调用您的函数。要么让你的 typedef 公开,要么让它们受到保护,同时也让函数受到保护。我更喜欢前者,因为在这里向用户公开 typedef 有什么害处? (有关我的意思的更多信息,请参阅this code example

标签: c++ templates typedef


【解决方案1】:

我想我理解发生这种情况的原因。考虑一个更简单的示例来演示相同的问题

template<class T>
struct base {
    typedef int foo;
};

/*
struct other{};

template<>
struct base<float> {
    typedef other foo;
};
*/

template<class T>
struct test {
    static typename base<T>::foo bar();
};

template<class T>
int test<T>::bar() {   // (1)
    return 2;
}

这会在 gcc 中产生以下错误:

a.cpp:22:5: error: prototype for ‘int test<T>::bar()’ does not match any in class ‘test<T>’
 int test<T>::bar() {
     ^
a.cpp:18:34: error: candidate is: static typename base<T>::foo test<T>::bar()
     static typename base<T>::foo bar();
                                  ^

为什么会这样?考虑一下编译器在遇到第 (1) 行时会怎么想。即使没有进行模板实例化(因此编译器没有任何东西可以替代T),它可能会尝试将函数定义的标头与其声明相匹配。它看到在定义中返回类型是int,而在声明中返回类型是typename base&lt;T&gt;::foo。编译器如何知道它们将是同一类型对于所有Ts?

显然编译器无法推断这一点,因为它根本不正确。事实上,如果您取消注释注释部分,您会发现对于 T=floatbase&lt;T&gt;::foo 将是 other,而不是 int

因此,当编译器遇到第 (1) 行时,它永远无法确定 对于每个 T base&lt;T&gt;::foo 将是 int。 (事实上​​,对于某些Ts,base&lt;T&gt; 可能根本没有foo 成员。)因此,如果编译器尝试在实际实例化之前检查返回类型,它没有其他但是失败了。

我不太擅长标准,所以我不能通过引用来支持这种解释;也许我实际上是错的,基于标准还有另一个原因。

无论如何,正如我在评论中指出的那样,您根本不应该使用 StaticInternalVersatileVector&lt;Item*, Number &gt;* 作为返回类型。最简单最正确的方法是只使用typename A&lt;Number&gt;::List*

【讨论】:

    猜你喜欢
    • 2019-12-14
    • 2020-10-02
    • 2015-02-27
    • 1970-01-01
    • 2019-12-14
    • 2011-11-30
    • 2021-12-20
    • 2022-01-03
    • 2019-06-11
    相关资源
    最近更新 更多