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