GCC 问题:使用依赖于模板参数的基类成员

发布于 2024-07-04 05:01:28 字数 574 浏览 15 评论 0 原文

以下代码不能使用 gcc 编译,但可以使用 Visual Studio 编译:

template <typename T> class A {
public:
    T foo;
};

template <typename T> class B: public A <T> {
public:
    void bar() { cout << foo << endl; }
};

我收到错误:

test.cpp:在成员函数'void B::bar()'中:

test.cpp:11: 错误:'foo' 未在此范围内声明

但它应该是! 如果我将 bar 更改为,

void bar() { cout << this->foo << endl; }

那么它编译,但我认为我不必这样做。 GCC 是否遵循 C++ 官方规范中的某些内容,或者这只是一个怪癖?

The following code doesn't compile with gcc, but does with Visual Studio:

template <typename T> class A {
public:
    T foo;
};

template <typename T> class B: public A <T> {
public:
    void bar() { cout << foo << endl; }
};

I get the error:

test.cpp: In member function ‘void B::bar()’:

test.cpp:11: error: ‘foo’ was not declared in this scope

But it should be! If I change bar to

void bar() { cout << this->foo << endl; }

then it does compile, but I don't think I have to do this. Is there something in the official specs of C++ that GCC is following here, or is it just a quirk?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

羁〃客ぐ 2024-07-11 05:01:28

大卫·乔伊纳有过这样的历史,原因如下。

编译 B 时的问题是编译器无法识别其基类 A,它是一个模板类,因此编译器无法知道来自基类的任何成员。

早期版本通过实际解析基模板类来进行一些推理,但 ISO C++ 表示这种推理可能会导致不应该发生的冲突。

在模板中引用基类成员的解决方案是使用 this (就像您所做的那样)或专门命名基类:

template <typename T> class A {
public:
    T foo;
};

template <typename T> class B: public A <T> {
public:
    void bar() { cout << A<T>::foo << endl; }
};

更多信息请参见 gcc 手册

David Joyner had the history, here is the reason.

The problem when compiling B<T> is that its base class A<T> is unknown from the compiler, being a template class, so no way for the compiler to know any members from the base class.

Earlier versions did some inference by actually parsing the base template class, but ISO C++ stated that this inference can lead to conflicts where there should not be.

The solution to reference a base class member in a template is to use this (like you did) or specifically name the base class:

template <typename T> class A {
public:
    T foo;
};

template <typename T> class B: public A <T> {
public:
    void bar() { cout << A<T>::foo << endl; }
};

More information in gcc manual.

对岸观火 2024-07-11 05:01:28

哇。 C++ 的怪异总是让我感到惊讶。

在模板定义中,非限定名称将不再查找依赖基的成员(如 C++ 标准中的 [temp.dep]/3 所指定)。 例如,

template <typename T> struct B {
  int m;
  int n;
  int f ();
  int g ();
};
int n;
int g ();
template <typename T> struct C : B<T> {
  void h ()
  {
    m = 0; // error
    f ();  // error
    n = 0; // ::n is modified
    g ();  // ::g is called
  }
};

您必须使名称具有相关性,例如在它们前面加上 this-> 前缀。 这是 C::h 的更正定义,

template <typename T> void C<T>::h ()
{
  this->m = 0;
  this->f ();
  this->n = 0
  this->g ();
}

作为替代解决方案(遗憾的是不能向后兼容 GCC 3.3),您可以使用 using 声明来代替此->:

template <typename T> struct C : B<T> {
  using B<T>::m;
  using B<T>::f;
  using B<T>::n;
  using B<T>::g;
  void h ()
  {
    m = 0;
    f ();
    n = 0;
    g ();
  }
};

这太疯狂了。 谢谢,大卫。

这是他们引用的标准 [ISO/IEC 14882:2003] 的“temp.dep/3”部分:

在类模板或类模板成员的定义中,如果类模板的基类依赖于模板参数,则在定义点的非限定名称查找期间不会检查基类范围类模板或成员的或者在类模板或成员的实例化期间。 [示例:

typedef double A; 
template<class T> class B { 
    typedef int A; 
}; 
template<class T> struct X : B<T> { 
    A a; // a has typedouble 
}; 

X 定义中的类型名称 A 绑定到全局命名空间范围中定义的 typedef 名称,而不是绑定到基类中定义的 typedef 名称类B。 ] [示例:

struct A { 
    struct B { /* ... */ }; 
    int a; 
    int Y; 
}; 
int a; 
template<class T> struct Y : T { 
    struct B { /* ... */ }; 
    B b; //The B defined in Y 
    void f(int i) { a = i; } // ::a 
    Y* p; // Y<T> 
}; 
Y<A> ya; 

模板参数 A< 的成员 A::BA::aA::Y /code> 不影响 Y 中名称的绑定。 ]


Wow. C++ never ceases to surprise me with its weirdness.

In a template definition, unqualified names will no longer find members of a dependent base (as specified by [temp.dep]/3 in the C++ standard). For example,

template <typename T> struct B {
  int m;
  int n;
  int f ();
  int g ();
};
int n;
int g ();
template <typename T> struct C : B<T> {
  void h ()
  {
    m = 0; // error
    f ();  // error
    n = 0; // ::n is modified
    g ();  // ::g is called
  }
};

You must make the names dependent, e.g. by prefixing them with this->. Here is the corrected definition of C::h,

template <typename T> void C<T>::h ()
{
  this->m = 0;
  this->f ();
  this->n = 0
  this->g ();
}

As an alternative solution (unfortunately not backwards compatible with GCC 3.3), you may use using declarations instead of this->:

template <typename T> struct C : B<T> {
  using B<T>::m;
  using B<T>::f;
  using B<T>::n;
  using B<T>::g;
  void h ()
  {
    m = 0;
    f ();
    n = 0;
    g ();
  }
};

That's just all kinds of crazy. Thanks, David.

Here's the "temp.dep/3" section of the standard [ISO/IEC 14882:2003] that they are referring to:

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. [Example:

typedef double A; 
template<class T> class B { 
    typedef int A; 
}; 
template<class T> struct X : B<T> { 
    A a; // a has typedouble 
}; 

The type name A in the definition of X<T> binds to the typedef name defined in the global namespace scope, not to the typedef name defined in the base class B<T>. ] [Example:

struct A { 
    struct B { /* ... */ }; 
    int a; 
    int Y; 
}; 
int a; 
template<class T> struct Y : T { 
    struct B { /* ... */ }; 
    B b; //The B defined in Y 
    void f(int i) { a = i; } // ::a 
    Y* p; // Y<T> 
}; 
Y<A> ya; 

The members A::B, A::a, and A::Y of the template argument A do not affect the binding of names in Y<A>. ]

撞了怀 2024-07-11 05:01:28

这在 gcc-3.4 中发生了变化。 C++ 解析器在该版本中变得更加严格——根据规范,但对于拥有遗留或多平台代码库的人来说仍然有点烦人。

This changed in gcc-3.4. The C++ parser got much more strict in that release -- per the spec but still kinda annoying for people with legacy or multi-platform code bases.

木落 2024-07-11 05:01:28

C++ 在这里不能假设任何事情的主要原因是基础模板可以稍后专门用于某种类型。 继续原来的例子:

template<>
class A<int> {};

B<int> x; 
x.bar();//this will fail because there is no member foo in A<int>

The main reason C++ cannot assume anything here is that the base template can be specialized for a type later. Continuing the original example:

template<>
class A<int> {};

B<int> x; 
x.bar();//this will fail because there is no member foo in A<int>
固执像三岁 2024-07-11 05:01:28

VC 没有实现两阶段查找,而 GCC 实现了。 因此,GCC 在实例化模板之前对其进行解析,因此比 VC 发现更多错误。
在您的示例中, foo 是一个从属名称,因为它依赖于“T”。 除非您告诉编译器它来自哪里,否则它在实例化模板之前根本无法检查模板的有效性。
这就是为什么你必须告诉编译器它来自哪里。

VC doesn't implemented two-phase lookup, while GCC does. So GCC parses templates before they are instantiated and thus finds more errors than VC.
In your example, foo is a dependent name, since it depends on 'T'. Unless you tell the compiler where it comes from, it cannot check the validity of the template at all, before you instantiate it.
That's why you have to tell the compiler where it comes from.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文