C++ C++;模板隐藏父成员
通常,当C++ C++;模板隐藏父成员,c++,generics,templates,C++,Generics,Templates,通常,当A从B继承时,A的所有成员自动对B的函数可见,例如 class A { protected: int a; }; class B : public A { int getA() {return a;} //no need to use A::a, it is automatically visible }; 但是,当我使用模板继承时,此代码将变得非法(至少在gcc) 为什么会这样?有没有其他方法来阻止C++隐藏父模板类的变量? 编辑:感谢大家的精彩讨论。我必须
A
从B
继承时,A
的所有成员自动对B
的函数可见,例如
class A {
protected:
int a;
};
class B : public A {
int getA() {return a;}
//no need to use A::a, it is automatically visible
};
但是,当我使用模板继承时,此代码将变得非法(至少在gcc
)
为什么会这样?有没有其他方法来阻止C++隐藏父模板类的变量?
编辑:感谢大家的精彩讨论。我必须承认我没有遵循引用C++标准段落的论点。我很难在没有阅读真实来源的情况下理解它
为了总结这次讨论,我能做的最好的事情就是引用“Python的禅宗”中的一句短句:
如果难以实施
解释一下,这(可能)是个坏主意
你也可以
class B : public A<T> {
int getA() {return this->a;}
};
B类:公共A类{
int getA(){返回this->a;}
};
问题在于成员位于基中,这取决于模板参数。正常的非限定查找是在定义点执行的,而不是在实例化点执行的,因此它不会搜索依赖基。这是一个常见的问题,但是完全可以绕过它,无论是函数、类型还是属性 问题出现在模板类和函数的两个阶段评估的实现中。标准通常要求对模板进行两次评估:
- 第一次遇到时,验证其格式是否正确
- 第二种方法是实例化,以实际生成给定类型的代码
a
成员。任何不依赖于模板参数之一的符号都应明确定义并进行检查
通过明确定义范围,您将使符号依赖于模板参数,从而将检查延迟到第二次计算
使用Boost
作为灵感:
template <class A1, class A2, class A3>
class MyClass: public Base<A1,A2,A3>
{
public:
typedef Base<A1,A2,A3> base_;
void foo()
{
// Accessing type
bar_type x; // ERROR: Not dependent on template parameter
typename base_::bar_type x;
// Accessing method
bar(); // ERROR: Not dependent on template parameter
this->bar();
base_::bar();
// Accessing attribute
mBar; // ERROR: Not dependent on template parameter
this->mBar;
base_::mBar;
};
}; // class MyClass
模板
类MyClass:公共基
{
公众:
typedef Base_u2;;
void foo()
{
//访问类型
bar_type x;//错误:不依赖于模板参数
typename base_uu::bar_x类型;
//访问方法
bar();//错误:不依赖于模板参数
这个->条();
base_u2;::bar();
//访问属性
mBar;//错误:不依赖于模板参数
这个->mBar;
基地:姆巴;
};
}; // 类MyClass
我喜欢
Boost
定义base.
内部typedef的习惯用法。首先,它当然有助于定义构造函数,其次,通过显式限定基类的内容,它让那些正在处理代码的人明白了这一点。因为存在关于非限定名称如何依赖的问题,或者非限定名称查找如何应用于依赖名称的问题:
Trail parse:确定如何解析语句 如果在模板中遇到依赖名称,则始终假定不命名类型,除非适用的名称查找发现它是一个类型,或者我们在名称前面加上
typename
:
template<typename T>
void f() {
T f0; // T is a template type parameter => type
T *f1;
typename T::name g1; // "name" is assumed to be a type.
T::name g0; // "name" cannot be looked up here => non-type
}
标准使非限定的foo
成为依赖的,因为参数T()
是类型依赖的。在实例化时,我们将在模板定义周围使用非限定名称查找来查找名为foo
的函数,并在模板定义和实例化点(在main
之后)周围使用依赖参数的查找(粗略地说,在T
的命名空间中)。依赖于参数的查找将找到foo
如果Bar
现在具有依赖基类,则非限定查找必须忽略该依赖基类:
template<typename T>
struct HasFoo { };
template<typename T>
struct Bar : HasFoo<T> {
void bar() { foo(T()); }
};
namespace A {
struct Baz { };
void foo(Baz); // found!
}
template<>
struct HasFoo<A::Baz> {
void foo();
};
int main() { Bar<A::Baz> b; b.bar(); }
该标准规定,在查找
运算符foo
名称中的foo
时,我们将在p->
范围内(在类A
范围内)和完整表达式出现的范围内(作为非限定名称的C::C
范围内)独立查找,并比较这两个名称,如果找到,它们是否指定相同的类型。如果我们在完整表达式范围内的查找过程中不忽略依赖基类A
,我们将在两个基类中发现foo
,因此具有歧义性。忽略A
将意味着我们在查找p->
时只找到一次名称,在查找TypeNameSugar
时再找到一次名称 C++常见问题解答Lite是一个很好的信息源。模板部分描述了这个问题:@UncleBens谢谢,我希望我能接受评论。我以为它会在那里,但没有找到。我已向FAQ lite作者发送了一封邮件:。另外,一个缺陷报告被发送到GCC,因为它不能正确地处理这个问题,我想:我现在明白为什么会发生这种情况了。但我不明白为什么标准是这样设计的。如果我在模板中,为什么我不想在模板实例化中查找内容呢。我能想到的唯一一件事是,它使编译器的实现更容易。次要nit:14.6.2依赖名称:第3段:在类模板或类模板成员的定义中,如果类模板的基类依赖于模板参数,在非限定名称查找过程中,无论是在类模板或成员的定义点,还是在类模板或成员的实例化过程中,基类作用域都不会被检查。标准似乎预见到模板会被解析两次:在第一次通过时,编译器会检查任何非依赖项,当实例化时,它还检查依赖的东西。(但VC++似乎在很大程度上忽视了这一点。)
template <class A1, class A2, class A3>
class MyClass: public Base<A1,A2,A3>
{
public:
typedef Base<A1,A2,A3> base_;
void foo()
{
// Accessing type
bar_type x; // ERROR: Not dependent on template parameter
typename base_::bar_type x;
// Accessing method
bar(); // ERROR: Not dependent on template parameter
this->bar();
base_::bar();
// Accessing attribute
mBar; // ERROR: Not dependent on template parameter
this->mBar;
base_::mBar;
};
}; // class MyClass
template<typename T>
void f() {
T f0; // T is a template type parameter => type
T *f1;
typename T::name g1; // "name" is assumed to be a type.
T::name g0; // "name" cannot be looked up here => non-type
}
template<typename T>
struct Bar {
void bar() { foo(T()); }
};
namespace A {
struct Baz { };
void foo(Baz); // found!
}
int main() { Bar<A::Baz> b; b.bar(); }
template<typename T>
struct HasFoo { };
template<typename T>
struct Bar : HasFoo<T> {
void bar() { foo(T()); }
};
namespace A {
struct Baz { };
void foo(Baz); // found!
}
template<>
struct HasFoo<A::Baz> {
void foo();
};
int main() { Bar<A::Baz> b; b.bar(); }
template<typename>
struct A {
typedef int foo;
operator int() {
return 0;
}
};
// makes sure applicable name-lookup
// classifies "foo" as a type (so parsing will work).
struct TypeNameSugar {
typedef int foo;
};
template<typename T>
struct C : A<T>, TypeNameSugar {
void c() {
A<T> *p = this;
int i = p->operator foo();
}
};
int main() {
C<void>().c();
}