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

Getting the address of template class object leads to full instantiation of template parameters

本文关键字:参数 实例化 地址 对象 获取      更新时间:2023-10-16

使用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;
}

不过,我发现这样的错误和变通办法完全不合逻辑。

分析/解释:

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

ADL是罪魁祸首。

假设II我怀疑这里有一个与ADL相关的东西(类可以有内联声明的静态自由函数(友元))。也许编译器需要实例化整个类模板,以确保它已经看到其中声明的运算符重载(以便进行重载解析)。

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

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

  • [snip]

  • 如果T是类类型(包括并集),则其关联类为:阶级本身;其所属类别(如有);及其直接和间接基类。其关联的命名空间是其关联的类是成员。此外,如果T是类模板specialization,其关联的命名空间和类还包括:命名空间和与模板类型关联的类为模板类型参数提供的参数(不包括模板模板参数);任何模板模板参数所在的命名空间是成员;以及任何成员模板用作模板的类模板参数是成员。

粗体短语包括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"(见上文)