使用 decltype() 和 SFINAE 时出错
为了回答..某个地方的其他问题,我编写了这段代码。
struct no_type{};
template<typename T> struct has_apply {
static decltype(T().apply<0u>(double())) func( T* ptr );
static no_type func( ... );
static const bool result = !std::is_same<no_type, decltype(func(nullptr))>::value;
};
class A {
public:
template< unsigned n >
void apply( const double& );
};
class B {
};
int main()
{
std::cout << std::boolalpha << has_apply< A >::result << '\n';
std::cout << std::boolalpha << has_apply< B >::result << '\n';
std::cin.get();
return( 0 );
}
现在在我看来,如果 T 提供接受双右值和模板参数文字的非静态成员函数“apply”,则结果应该为 true,否则为 false。然而,在编译 has_apply
时,给出的示例实际上无法针对 B 类进行编译。 decltype 语句中 T 的替换失败是否意味着它只是调用另一个函数?这不就是SFINAE的宗旨吗?
以有史以来最荒谬、最无意义的方式解决:
struct no_type{};
template<typename T> struct has_apply {
template<typename U> static decltype(U().apply<0u>(double())) func( U* );
template<typename U> static no_type func( ... );
static const bool result = !std::is_same<no_type, decltype(func<T>(nullptr))>::value;
};
class A {
public:
template< unsigned n >
void apply( const double& );
};
class B {
};
int main()
{
std::cout << std::boolalpha << has_apply< A >::result << '\n';
std::cout << std::boolalpha << has_apply< B >::result << '\n';
std::cin.get();
return( 0 );
}
In response to .. some other question somewhere, I wrote this code.
struct no_type{};
template<typename T> struct has_apply {
static decltype(T().apply<0u>(double())) func( T* ptr );
static no_type func( ... );
static const bool result = !std::is_same<no_type, decltype(func(nullptr))>::value;
};
class A {
public:
template< unsigned n >
void apply( const double& );
};
class B {
};
int main()
{
std::cout << std::boolalpha << has_apply< A >::result << '\n';
std::cout << std::boolalpha << has_apply< B >::result << '\n';
std::cin.get();
return( 0 );
}
Now it seems to me that result should be true if T offers a non-static member function "apply" that accepts a double rvalue and a template parameter literal, and false otherwise. However, the example given actually fails to compile for class B, when compiling has_apply<B>
. Shouldn't the fact that the substitution of T failed in the decltype statement mean that it simply calls the other func? Isn't that kind of the point of SFINAE?
Solved in the most ridiculous, pointless fashion ever:
struct no_type{};
template<typename T> struct has_apply {
template<typename U> static decltype(U().apply<0u>(double())) func( U* );
template<typename U> static no_type func( ... );
static const bool result = !std::is_same<no_type, decltype(func<T>(nullptr))>::value;
};
class A {
public:
template< unsigned n >
void apply( const double& );
};
class B {
};
int main()
{
std::cout << std::boolalpha << has_apply< A >::result << '\n';
std::cout << std::boolalpha << has_apply< B >::result << '\n';
std::cin.get();
return( 0 );
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当函数模板的模板参数替换失败时,SFINAE 适用,而不适用于具有相关(非模板)函数作为成员的类模板的模板参数,就像您的情况一样。
修复此问题后,您至少应该将
decltype(T().apply<0u>(double()))
更改为decltype(T().template apply<0u>(double( )))
因为T()
表达式是依赖类型。原因是这样的:当编译器第一次看到T().apply<0u>
时,它还不知道T
的任何信息,那么它应该如何解析.
之后的标记apply
和<
?apply
可能是一个成员模板,然后<
将为其启动参数列表。 OTOHapply
可能是非模板成员(例如数据成员),然后<
将被解析为“小于”运算符。存在歧义,编译器此时解决该问题还为时过早。程序员需要一种消歧机制来告诉编译器apply
应该是什么:是否是模板。这里出现了.template
(或->template
,或::template
)构造来救援:如果它存在,编译器知道它应该是模板成员,否则如果它不存在,则编译器知道该成员不应该是模板。最后,这是我创建的一个示例,它可以在 g++ 4.5.0 上使用
-std=c++0x
正常工作并产生所需的结果:输出为:
现在,如果您删除两个
.template 从第一个
f()
定义中,则输出变为:这表示
class A
的替换失败,因为它没有任何非名为apply
的模板成员。斯菲尼在行动!SFINAE applies when substitution fails for a function template's template parameter, not for a class template's template parameter that has the (non-template) function in question as a member, as is in your case.
After fixing that, you should at least change
decltype(T().apply<0u>(double()))
todecltype(T().template apply<0u>(double()))
becauseT()
expression is of a dependent type. The reason for that is this: when the compiler first seesT().apply<0u>
, it doesn't know anything aboutT
yet, so how should it parse the tokensapply
and<
after.
?apply
might be a member template, and then<
would start the argument list for it. OTOHapply
might instead be a non-template member (e.g. a data member), and then<
would be parsed as 'less-than' operator. There is an ambiguity, and it's still too early for the compiler to resolve that at this point. There is a need for a disambiguation mechanism a programmer could use to tell the compiler whatapply
is expected to be: a template or not. And here comes the.template
(or->template
, or::template
) construct to the rescue: if it's present, the compiler knows it should be a template member, otherwise if it's not present then the compiler knows the member shouldn't be a template.Finally here's an example I created that works correctly and produces the desired results on g++ 4.5.0 with
-std=c++0x
:Output is:
Now if you remove both
.template
from the firstf()
definition, then the output becomes:Which is to indicate substitution failure for
class A
as it doesn't have any non-template member namedapply
. SFINAE in action!