C++ 获取模板类对象的地址将导致模板参数的完全实例化
我用g++4.6和4.8编译这段代码时出错。 g++4.2和4.4可以。这是一个bug还是一些新的语言特性C++ 获取模板类对象的地址将导致模板参数的完全实例化,c++,templates,gcc,instantiation,C++,Templates,Gcc,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
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;
}
我发现这些错误和解决方法完全不合逻辑。分析/解释:
您看到的是浅实例化,而不是完整实例化(请参见下面的证据)
ADL是罪魁祸首
假设II我怀疑这里有一个与ADL相关的东西(类可以具有内联声明的静态自由函数(friends)。也许编译器需要实例化整个类模板,以确保它看到其中声明的运算符重载(以便进行重载解析)
标准在这里支持我:§3.4.2(n3337中的第46页):
²[snip]名称空间和类的集合完全由
函数参数的类型(以及任何模板的名称空间
模板参数)。[snip]名称空间和类的集合是
按以下方式确定:
- [剪报]
- 如果T是类类型(包括联合),则其关联的类为: 类本身;它所属的类(如有);及其直接和间接的 间接基类。其关联的命名空间是 它的关联类是成员。此外,如果T是类模板 专门化,其关联的名称空间和类还包括: 与模板类型关联的名称空间和类 为模板类型参数(不包括模板)提供的参数 模板参数);包含任何模板参数的名称空间 是成员;以及任何成员模板用作模板的类 模板参数是成员
class A
作为ADL的查找命名空间
解决方法:
在您的情况下,可以使用std::addressof(b)
来代替&b
,它会起作用
演示:
看
注:(不合格id)
技巧在本标准§3.4.2中定义)
此外,您还可以验证模板参数(A
)是否仅被实例化将格式不正确的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"; }
};
历史:
操作符&
是问题所在,但std::addressof()
还行李>
这让我想到了我的“假设II”(见上文)我发现令人惊讶的是,它实际上是编译的,而你却没有编译。我本以为通过实例化模板(声明
B
),你需要A
才有意义。我的问题是:为什么这个->条()有效,(*this).bar()有效,(&*this)->条()会出错?在我看来,模板参数U[=A]在B类中的任何地方都没有使用,因此根本不应该实例化它(惰性模板实例化)。然而,当一个人得到B对象的地址时,情况就不是这样了。这里看起来像是g++编译器的bug,不是吗?我还不知道为什么会发生这种情况,但gcc、clang和EDG的最新版本在行为上都是一致的,也就是说,我怀疑标准强制要求它。@NikkiChumakov你在其中看到我的更新了吗?我很想知道你是否解决了这个问题,证明这是ADL,使用增加的相关标准引用。我想这应该可以。(我会整理一下格式)好的,我明白了。感谢您对标准条款的引用和解释。@NikkiChumakov I补充了更准确的说明,这是一个没有完全实例化的例子,以及如何证明这一点。我缩小了示例,还显示了A
实际上是如何被用作freefunction(A)
中freefunction
的查找命名空间的:<代码>ADL在这里代码>@sehe很遗憾,我无法使用建议的解决方法。我不得不改用CRTP。它是有效的,但我并不真正理解它是如何工作的以及为什么工作的(-:请参阅我问题中的更新2。
#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;
}
#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!
template <typename T> struct A {
void uninstantiated() {
typedef typename T::value_type type;
}
friend void freefunction(B<A>&) { std::cout << "ADL was here!\n"; }
};
ADL was here!
ADL was here!