2011-10-18 65 views
3

为什么有些编译器会坚持模板基类的合格成员公共成员,而对非模板类不要求相同呢?请看下面代码:模板类vs私有继承

模板类:

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

在上面的代码之间的显著差别是,虽然第一个列表使用模板类,第二个列表不。

现在,这两个列表将编译罚款与微软的Visual Studio编译器(CL),但首批上市WONT同时与数字火星编译(DMC)和编译简约GNU为Windows(MinGW的 - g ++)编译器。我会得到一个错误,如“vval_未在范围中声明” - 一个错误,我很明白它的含义。

如果我有资格获得使用TestImpl <牛逼> :: vval_代码工作的TestImpl的公共变量vval_。在第二个列表中,当派生类访问基类'vval_变量而不限定它时,编译器不会抱怨。

对于两种编译器可能还有其他人,我的问题是,为什么我应该能够从非模板类与非模板类继承直接访问(不符合条件)vval_变量直接,而我不能从做同样的模板类继承自模板类

+2

您也可以限定'vval_':'this-> vval_'。 –

+2

http://www.parashift.com/c++-faq-lite/templates.html#faq-35.19 – visitor

+0

@Rob:this-> vval_适用于派生类。当使用“以某种类型的继承来实现”时,如果您的实现的重要部分位于基础claas中,那么只需要执行此操作 - 每个地方都会感到不便。谢谢 –

回答

2

的问题,你所面临的是编译器vval_不是一个从属名称,所以它会尝试在模板实际实例化之前查找它。此时,编译器[*]还不知道基本类型,因此它不考虑模板化基础。 Visual Studio不执行两阶段查找,因此这不是必需的。

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

或者,您可以使用TestImpl<T>::vval_来限定标识符所属的类型,如@mrozenau所示。再次,这使得标识符依赖于模板参数T并且查找被推迟。虽然这两种方法都有助于延迟查找的最终目的,但第二种方法会带来额外的副作用,即动态分派将被禁用。在这种情况下,这并不重要,但如果vval_实际上是虚拟功能,那么this->f()将调用最终的覆盖,而TestImpl<T>::f()将执行TestImpl<T>中的覆盖。

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

6

必须限定vval_TestImpl<T>告诉它Test<T>取决于实际类型的T编译器(可能存在的Test<T>定义之前宣布TestImpl<T>一些局部/明确的专业化和它的实例,将改变的vval_在这方面的意思。为了让编译器知道的是,你必须告诉vval_是(模板参数)相关。

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

3

MSVC(微软...)从来就不是符合标准,当涉及到的模板代码,所以他是鹤立鸡群:)

的问题是,模板在两个阶段解析:

  • 当模板被解析时,执行第一阶段,并且当所述模板被实例化
不依赖(明确地)上模板参数的任何标识符,应解决
  • 执行第二阶段

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

    一个简单的补救措施是限定vval_,通常与this->,将其标记为从属。

  • +0

    实际上这并不是那么糟糕,唯一缺少的东西(标准要求)是两阶段名称查找。并不要求是两阶段解析,例如,人们可以想象编译器的实现,它只会记住模板定义处的上下文,并将其用于实例化时的专有名称查找。 –

    +0

    @KonstantinOznobihin:我不同意*不那么差*。名称查找类似于重载分辨率:对于总是选择最后可能的重载的编译器会怎么说?我明白他们可能不希望用户使用依赖名称问题来进行可用性,但是您可以在不中断查找的情况下执行此操作:x –

    +0

    @KonstantinOznobihin:嗯...标准确定模板上下文中的依赖类型必须前面加上'typename'关键字(或者带有'template'关键字的依赖模板),并且VS忽略该要求,这使得编写非可移植代码变得非常容易...这一切都取决于你如何看待它 –