获取模板类对象的地址将导致模板参数的完全实例化 [英] Getting the address of template class object leads to full instantiation of template parameters

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

问题描述

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

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

模板< typename U>
struct b
{
void bar(){}
void foo()
{
// OK
this-> bar ();

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

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

int main()
{
B< A<无效> > b;
b.foo();
返回0;
}

g ++ inst.cc

  inst.cc:在'struct A< void>'的实例化中:
inst.cc:20:5:需要从'void B< U> :: foo ()[with U = A< void>'
inst.cc:27:10:从这里需要
inst.cc:3:34:错误:'void'不是类,struct ,或者联合类型
typedef typename T :: value_type type;

更新1 :A无法实例化,I知道。



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



更新2

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



我不想重写或重新创建 std :: bind ,所以我必须使用 CRTP 才能正常工作:

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

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

void bar(){}
void foo()
{
//最近编译器出错。
// std :: tr1 :: bind(& B :: bar,this)();

//现在确定
std :: tr1 :: bind(& Derived :: bar,derived())();
}
};

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

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

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

解决方案

分析/解释:



/ em>实例化,而不是完整的(见下面的证明)。

ADL是这里的罪魁祸首。


假设II 我在这里怀疑ADL相关的东西(类可以有静态自由函数(朋友)声明为inline。也许编译器需要实例化整个类模板为了确保它已经看到了在它声明的操作符重载(为了做重载解析)。

标准支持我在这里:§3.4.2(p33 in n3337):


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



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

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

    您可以使用 std :: addressof(b)来代替 & b 并且它可以工作。



    示范: href =http://liveworkspace.org/code/4f85a06598eebe1d8060112be36f4a29 =noreferrer> http://liveworkspace.org/code/4f85a06598eebe1d8060112be36f4a29



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

      #include< vector> 
    #include< iostream>

    struct Base {};

    模板< typename U> struct B:Base {};

    模板< typename T> struct A {
    typedef typename T :: value_type type;
    朋友void freefunction(B&&){std :: cout<< ADL在这里!\ n; }
    };

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

    int main()
    {
    B< A<的std ::矢量< INT> > >一个;
    B< A<无效> > b;

    //围着parens防止ADL:
    (freefunction)(a);
    (免费功能)(b); //选择:: freefunction(Base&)

    freefunction(a); // ADL选择朋友内联自由函数(B std :: vector >>&)
    // freefunction(b); // ADL失败:模板参数不能(浅)实例化
    }

    打印 p>

      ADL在这里! 

    另外,您可以验证模板参数( A< void> / code>)仅获取浅实例将格式不正确的typedef移入成员函数可消除问题:

     模板< typename T> ; struct A {
    void uninstantiated(){
    typedef typename T :: value_type type;
    }
    朋友void freefunction(B&&){std :: cout<< ADL在这里!\ n; }
    };

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

      ADL在这里! 
    ADL在这里!






    历史记录:


    1. 我注意到 operator& 是问题,但是 std :: addressof() code> ok!

    2. 我注意到使用任何(重载)运算符似乎都触发了这种行为

    3. ol>

      这导致我进入了我的'假设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天全站免登陆