模板类与私有继承

时间:2011-10-18 13:25:41

标签: c++ oop inheritance

为什么某些编译器会坚持要求模板基类的成员公共成员,而非模板类不需要相同?请查看以下代码清单:

模板类:

#include <iostream>

using namespace std;

template <class T>
class TestImpl {
public: // It wont make a difference even if we use a protected access specifier here
    size_t vval_;
    TestImpl(size_t val = 0) : vval_(val) { }
};

template <class T>
class Test : public TestImpl<T> {
public:
    Test(size_t val) : TestImpl<T>(val) {
        cout << "vval_ : " << vval_ << endl; // Error: vval_ was not declared in this scope
        //! cout << "vval_ : " << TestImpl<T>::vval_ << endl; // this works, obviously
    }
};

int main() {
    Test<int> test1(7);

    return 0;
}

非模板类:

#include <iostream>

using namespace std;

class TestImpl {
public: // It wont make a difference even if we use a protected access specifier here
    TestImpl(size_t val = 0) : vval_(val) {}
    size_t vval_;
};

class Test : public TestImpl {
public:
    Test(size_t val) : TestImpl(val) {
        cout << "vval_ : " << vval_ << endl;
    }
};

int main() {
    Test test1(7);

    return 0;
}

上述代码清单之间的显着差异是,虽然第一个清单使用模板类,但第二个清单没有。

现在,两个列表都可以使用 Microsoft的Visual Studio编译器(cl)进行编译,但第一个列表 WONT 使用 Digital Mars Compiler (dmc)和极简主义GNU for Windows (MinGW - g ++)编译器。我会得到一个错误,例如“vval_未在范围内声明” - 这个错误我很明白这意味着什么。

如果我使用 TestImpl&lt; T&gt; :: vval _ 限定访问 TestImpl 的公共变量 vval _ ,则代码可以正常运行。在第二个清单中,当派生类访问基类“ vval _ ”变量而没有限定它时,编译器不会抱怨。

关于这两个编译器以及可能的其他编译器,我的问题就是为什么我应该能够直接从非模板类继承访问(强制) vval _ 变量。一个非模板类,虽然我不能从从模板类继承的模板类做同样的事情?

3 个答案:

答案 0 :(得分:6)

您必须使用vval_限定TestImpl<T>,告诉编译器它取决于TTest<T>的实际类型(可能有部分/显式)在TestImpl<T>的定义之前声明的Test<T>的特化以及它将在该上下文中改变vval_的含义的实例化。为了使编译器意识到这一点,你必须告诉它vval_依赖于(模板参数)。

另见http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html

答案 1 :(得分:3)

MSVC(微软......)在模板代码方面从未符合标准,所以他是奇怪的:)

问题是模板分两个阶段进行解析:

  • 解析模板时执行第一阶段,并且应解析任何不依赖于(明确)模板参数的标识符
  • 在模板实例化时执行第二阶段

在您的情况下,第一阶段失败,因为vval_未明确依赖于模板参数(不是依赖名称),因此它应该可用。

一个简单的补救措施是通过vval_来限定this->,以将其标记为依赖。

答案 2 :(得分:2)

您面临的问题是,编译器vval_不是依赖名称,因此它会尝试在具有该类型的模板的实际实例化之前查找它。此时,编译器 [*] 尚不知道基类型,因此它不考虑模板化基数。 Visual Studio不执行两阶段查找,因此不需要这样做。

解决方案是将标识符转换为依赖标识符,这可以通过多种方式之一完成。最简单和建议使用this(如this->vval_中所述)。通过添加显式this,编译器知道vval_可以根据模板参数而不同,它现在是依赖名称,并且它将查找推迟到第二阶段(在论证替换之后)。

或者,您可以使用TestImpl<T>::vval_限定标识符所属的类型,如@mrozenau所建议的那样。同样,这使得标识符依赖于模板参数T并且查询被推迟。虽然它们都服务于将查询推迟到以后的最终目的,但是第二种方法具有额外的副作用,即动态调度将被禁用。在这种特殊情况下,无关紧要,但如果vval_实际上是虚函数,那么this->f()将调用最终覆盖,而TestImpl<T>::f()将执行TestImpl<T>中存在的覆盖

[*] 在模板的第一阶段验证期间,在将参数替换为模板之前,基本类型尚不可知。原因是不同的参数集可能会触发选择基本模板的不同特化。