9

使用 g++ 4.6 和 4.8 编译此代码时出现错误。g++ 4.2 和 4.4 都可以。这是一个错误还是一些新的语言功能?

template <typename T>
struct A { typedef typename T::value_type type; };

template <typename U>
struct B
{
  void bar () { }
  void foo ()
  {
    // OK
    this->bar ();

    // OK
    (*this).bar ();

    // Error in g++ 4.6-4.8 
    // leads to full instantiating of template arg "U"
    (&*this)->bar ();
  }
};

int main ()
{
  B< A<void> > b;
  b.foo ();
  return 0;
}

g++ inst.cc

inst.cc: In instantiation of ‘struct A<void>’:
inst.cc:20:5:   required from ‘void B<U>::foo() [with U = A<void>]’
inst.cc:27:10:   required from here
inst.cc:3:34: error: ‘void’ is not a class, struct, or union type
   typedef typename T::value_type type;
                                  ^

更新 1:我知道 A 不能被实例化。

问题是:为什么编译器试图在“(&*this)->bar ()”行而不是在“this->bar ()”或“(*this).bar ()”行实例化它?

更新 2

建议的解决方法addressof (object)对我不起作用,因为实际上我在尝试使用std::bind (&B::bar, this). 真正的代码当然要复杂得多,并且bind没有单独使用,但问题是追溯到简单的std::bind表达式。

我不想重写或重新发明std::bind,所以我不得不使用CRTP让它工作:

#include <tr1/functional>
template <typename T>
struct A { typedef typename T::value_type type; };

template <typename Derived, typename U>
struct B
{
  Derived* derived (void) { return static_cast<Derived*>(this); }

  void bar () { }
  void foo ()
  {
    // error with recent compiler.
    // std::tr1::bind (&B::bar, this) ();

    // now ok
    std::tr1::bind (&Derived::bar, derived ()) ();
  }
};

struct C: B<C, A<void> >
{
};

int main ()
{
  C c;
  c.foo ();
  return 0;
}

我发现这样的错误和解决方法是完全不合逻辑的。

4

1 回答 1

11

分析/说明:

您看到的是实例化,而不是完整的实例化(参见下面的证明)。

ADL 是这里的罪魁祸首。

假设 II我在这里怀疑与 ADL 相关的事情(类可以具有内联声明的静态自由函数(朋友)。也许编译器需要实例化整个类模板以确保它已经看到其中声明的运算符重载(为了做重载决议)。

标准在这里支持我:§3.4.2(n3337 中的 p46):

² [snip]命名空间和类的集合完全由函数参数的类型(以及任何模板模板参数的命名空间)决定。[snip]命名空间和类的集合通过以下方式确定:

  • [剪辑]

  • 如果 T 是类类型(包括联合),则其关联的类是:类本身;它所属的类别(如有的话);及其直接和间接基类。其关联名称空间是其关联类是其成员的名称空间。此外,如果 T 是类模板特化,则其关联的命名空间和类还包括:与为模板类型参数(不包括模板模板参数)提供的模板实参类型相关联的命名空间和类;任何模板模板参数都是其成员的命名空间;以及用作模板模板参数的任何成员模板是其成员的类。

粗体短语包括class A<void>作为 ADL 的查找名称空间。

解决方法:

在您的情况下std::addressof(b)可以使用,而不是&b它会工作。

示范:

http://liveworkspace.org/code/4f85a06598eebe1d8060112be36f4a29

注意:(unqualified-id)技巧在标准的§3.4.2中定义)

#include <vector>
#include <iostream>

struct Base {};

template <typename U> struct B : Base { };

template <typename T> struct A {
    typedef typename T::value_type type;
    friend void freefunction(B<A>&) { std::cout << "ADL was here!\n"; }
};

void freefunction(Base& /*acceptAll*/) {}

int main ()
{
    B< A<std::vector<int> > >  a;
    B< A<void> >               b;

    // surrounding with parens prevents ADL:
    (freefunction)(a);
    (freefunction)(b); // selects ::freefunction(Base&)

    freefunction(a);   // ADL selects friend inline freefunction(B< A<std::vector<int> > >&)
  //freefunction(b);   // ADL fails: template arg cannot be (shallow) instantiated
}

印刷

ADL was here!

此外,您可以验证模板参数 ( A<void>) 是否仅被浅实例化将格式错误的 typedef 移动到成员函数中可以解决问题:

template <typename T> struct A {
    void uninstantiated() {
        typedef typename T::value_type type;
    }
    friend void freefunction(B<A>&) { std::cout << "ADL was here!\n"; }
};

输出(http://liveworkspace.org/code/a15c933293281d0926e8b1ff39180079

ADL was here!
ADL was here!

历史:

  1. 我注意到operator&是问题所在,但std::addressof()没关系!
  2. 我注意到使用任何(重载)运算符似乎都会触发这种行为

这将我引向我的“假设 II”(见上文)

于 2012-10-16T19:01:52.463 回答