据我所知,模板成员函数仅在使用时生成。如果不是所有使用的类型都支持这样的功能,这很方便。但是,这似乎不适用于具有尾随返回类型规范的函数。以下是一个小实验:
// helper function for case A workaround
template <typename A, typename T>
auto F(T&& x)
-> decltype(x.template f <A>())
{ return x.template f <A>(); }
// helper function for case B workaround
template <typename A, typename T>
auto G(T&& x)
-> decltype(x.g())
{ return x.g(); }
template <typename T>
struct S
{
// case A: not ok in GCC + Clang
template <typename A>
auto f1()
-> decltype(T().template f <A>())
{ return T().template f <A>(); }
// case A workaround: ok in Clang + GCC
template <typename A>
auto f2()
-> decltype(F <A>(T()))
{ return F <A>(T()); }
// case B: ok in GCC, not ok in Clang
template <typename A>
auto g1()
-> decltype(T().g())
{ return T().g(); }
// case B workaround: ok in GCC + Clang
template <typename A>
auto g2()
-> decltype(G <A>(T()))
{ return G <A>(T()); }
};
请注意,此示例仅用于说明问题,但在其他任何方面都无用。
S <T>
可以针对具有适当成员函数T
,f
的任何类型g
进行实例化。
但是,如果我尝试实例化S <int>
,例如按S <int> s{};
,我收到type 'int' is not a structure or union
之类的错误。对于f1
,g1
这两种情况都会发生这种情况,这两种情况分别尝试在类型f
的值上调用模板函数g
或非模板函数T
(在这种情况下int
)。即使我没有尝试在对象f1
上调用g1
或s
,也会发生这种情况。但是,GCC对g1
的情况很好; Clang不是。
案例A(模板成员函数f
)的解决方法是使用辅助函数F
,这是f2
所做的,并且对于Clang和GCC都可以正常工作。它似乎有效,因为调用T().template f <A>()
对于f2
的声明是隐藏的,当类型F <A>(T())
未知时,编译器不会调查A
。
案例B(非模板成员函数g
)的相同解决方法也适用于两个编译器。
我很感激帮助找出正在发生的事情。在每种情况下哪种行为是正确的?哪个编译器正确?一般还有其他解决方法吗?
我正在使用GCC 4.8.1和Clang 3.3。
答案 0 :(得分:1)
SFINAE仅适用于函数的模板参数,而不适用于从类继承的模板参数。
另一种解决方案是将副本T包含在第二个模板参数中,但这只不过是变通方法的缩短版本:
#include <utility>
#include <type_traits>
struct Foo {
template < typename T > T f() { return {}; }
};
template <typename T>
struct S {
template <typename A, typename TT = T >
auto f1() -> decltype(std::declval<TT>().template f <A>()) {
static_assert(std::is_same<T,TT>::value, "TT must be equal to T" );
return TT().template f <A>();
}
};
int main() {
S<Foo> a;
a.f1<int>(); // ok
S<int> b;
b.f1<int>(); // not ok
}