获取模板类对象的地址导致模板参数的完全独立 [英] Getting the address of template class object leads to full instatiation of template parameters

查看:208
本文介绍了获取模板类对象的地址导致模板参数的完全独立的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用g ++ 4.6和4.8编译此代码时出现错误。
g ++ 4.2和4.4可以。

 模板< 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();

// g ++中的错误4.6-4.8
//导致完全实例化模板argU
(& * this) - > bar
}
};

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

g ++ inst.cc

  inst.cc:在'struct A< void>'的实例化中:
inst.cc:20:5:required from'void B< U& ()[with U = A< void>]'
inst.cc:27:10:需要从这里
inst.cc:3:34:错误:'void'不是一个类,struct ,或联合类型
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()
{
//最近编译器错误。
// 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;
}

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

解决方案

分析/解释:



您看到的是<



ADL是这里的罪魁祸首。


假设II 我怀疑一个ADL相关的东西在这里(类可以有静态自由函数(朋友)声明内联。也许编译器需要实例化整个类模板


标准备份了我的订单这里:§3.4.2(n3337中的p46):


² em>这些命名空间和类的集合完全由
函数参数类型(以及任何模板
模板参数的命名空间)确定。 [snip] 命名空间和类的集合是以下列方式确定的




  • [snip]


  • 如果T是类类型(包括工会),其关联类为:
    类本身;它是其成员的类,如果有的话;和其直接和
    间接基类。其关联的命名空间是其相关联的类是
    的命名空间。此外,如果T是类模板
    specialization,其相关联的命名空间和类也包括:与模板类型相关联的
    命名空间和类
    为模板类型参数提供的参数(不包括模板
    模板参数);任何模板模板参数
    的命名空间是成员;并且任何用作模板
    模板参数的成员模板的类都是成员。



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





演示:



请参阅 http://liveworkspace.org/code/4f85a06598eebe1d8060112be36f4a29



注意:



p $ p> #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在这里!\\\
; }
};

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

int main()
{
B& A< std :: vector< int> > >一个;
B< A< void> > b;

//用括号括起来防止ADL:
(freefunction)(a);
(freefunction)(b); //选择:: freefunction(Base&)

freefunction(a); // ADL选择朋友内联自由功能(B //自由功能// ADL failed:template arg can not be(shallow)instantiated
}

打印



  ADL在这里! 

此外,您可以验证模板参数( A< void& / code>)仅获取浅实例化将错误格式的typedef移入成员函数会移除此问题:

  template< typename T& ; struct A {
void uninstantiated(){
typedef typename T :: value_type type;
}
friend void freefunction(B< A&){std :: cout< ADL在这里!\\\
; }
};

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

  ADL在这里! 
ADL在这里!






历史:


  1. 我注意到运算符& 是问题,但 std :: addressof code> is ok!

  2. 我注意到使用任何(重载的)运算符似乎会触发此行为



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


      I got the errors compiling this code with g++ 4.6 and 4.8. g++ 4.2 and 4.4 is OK. Is it a bug or some new language feature?

      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;
                                        ^
      

      Update 1: A cannot be instantiated, I know.

      The question is: why the compiler tries to instantiate it at "(&*this)->bar ()" line, but not at "this->bar ()" or "(*this).bar ()" lines?

      Update 2:

      The suggested workaround with addressof (object) is not working for me, because actually I got the error when I tried to use std::bind (&B::bar, this). The real code is much more complex of course and the bind was not used standalone, but the problem was traced to the simple std::bind expression.

      I did not want to rewrite or reinvent std::bind, so I had to use CRTP to make it work:

      #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;
      }
      

      I find such errors and workarounds to be completely illogical though.

      解决方案

      Analysis/explanation:

      What you are seeing is shallow instantiation, not full (see below for proof).

      ADL is the culprit here.

      Hypothesis II I'm suspecting an ADL-related thing here (classes can have static free functions (friends) declared inline. Perhaps the compiler needs to instantiate the whole class template in order to make sure it has seen the operator overloads declared in it (in order to do overload resolution).

      The standard backs me up here: §3.4.2 (p46 in n3337):

      ² [snip] The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). [snip] The sets of namespaces and classes are determined in the following way:

      • [snip]

      • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces of which its associated classes are members. Furthermore, if T is a class template specialization, its associated namespaces and classes also include: the namespaces and classes associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members.

      The bolded phrase includes class A<void> as a lookup namespace for ADL.

      Workaround:

      In your situation std::addressof(b) can be used instead of &b and it will work.

      Demonstration:

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

      Note: the (unqualified-id) trick is defined in §3.4.2 of the standard)

      #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
      }
      

      Prints

      ADL was here!
      

      Also, you can verify that the template argument (A<void>) gets shallow instantiated only. Moving the ill-formed typedef into a member function removes the problem:

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

      Outputs (http://liveworkspace.org/code/a15c933293281d0926e8b1ff39180079)

      ADL was here!
      ADL was here!
      


      History:

      1. I noticed operator& was the problem, but std::addressof() was ok!
      2. I noticed use of any (overloaded) operators seems to trigger this behaviour

      This lead me to my 'Hypothesis II' (see above)

      这篇关于获取模板类对象的地址导致模板参数的完全独立的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆