获取模板类对象的地址会导致模板参数的完全实例化

Nik*_*kov 9 c++ gcc templates instantiation

我用g ++ 4.6和4.8编译了这段代码的错误.g ++ 4.2和4.4就可以了.这是一个bug还是一些新的语言功能?

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;
}
Run Code Online (Sandbox Code Playgroud)

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;
                                  ^
Run Code Online (Sandbox Code Playgroud)

更新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;
}
Run Code Online (Sandbox Code Playgroud)

我发现这些错误和变通办法完全不合逻辑.

seh*_*ehe 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
}
Run Code Online (Sandbox Code Playgroud)

打印

ADL was here!
Run Code Online (Sandbox Code Playgroud)

此外,您可以验证模板argument(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"; }
};
Run Code Online (Sandbox Code Playgroud)

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

ADL was here!
ADL was here!
Run Code Online (Sandbox Code Playgroud)

历史:

  1. 我注意到operator&了问题,但std::addressof()还可以!
  2. 我注意到使用任何(重载)运算符似乎会触发此行为

这导致我的'假设II'(见上文)