获取模板类对象的地址导致模板参数的完全独立 [英] Getting the address of template class object leads to full instatiation of template parameters
问题描述
使用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在这里!
历史:
- 我注意到
运算符&
是问题,但 std :: addressof code> is ok!
- 我注意到使用任何(重载的)运算符似乎会触发此行为
这导致我的假设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:
- I noticed
operator&
was the problem, but std::addressof()
was ok!
- I noticed use of any (overloaded) operators seems to trigger this behaviour
This lead me to my 'Hypothesis II' (see above)
这篇关于获取模板类对象的地址导致模板参数的完全独立的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!