为了回应某些其他问题,我写了这段代码。
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提供接受双rvalue和模板参数文字的非静态成员函数“apply”,则结果应为true,否则为false。但是,在编译has_apply<B>
时,给出的示例实际上无法为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 );
}
答案 0 :(得分:3)
SFINAE适用于函数模板的模板参数替换失败,而不适用于具有(非模板)函数作为成员的类模板的模板参数,就像您的情况一样。
修复后,您至少应将decltype(T().apply<0u>(double()))
更改为decltype(T().template apply<0u>(double()))
,因为T()
表达式属于依赖类型。原因是:当编译器第一次看到T().apply<0u>
时,它对T
一无所知,那么它应如何解析标记apply
和<
在.
之后? apply
可能是成员模板,然后<
将为其启动参数列表。 OTOH apply
可能是非模板成员(例如数据成员),然后<
将被解析为'less-than'运算符。存在歧义,编译器现在解决这个问题还为时尚早。程序员可以使用消歧机制来告诉编译器预期apply
是什么:模板与否。这里有.template
(或->template
或::template
)构造来拯救:如果它存在,编译器知道它应该是模板成员,否则如果它不存在那么编译器知道该成员不应该是模板。
最后,这是我创建的一个正常工作的示例,并使用-std=c++0x
在g ++ 4.5.0上生成所需的结果:
#include <iostream>
template < class T >
decltype( T().template apply< 0u >( double() ) ) f( T &t )
{
return t.template apply< 0u >( 5. );
}
const char *f( ... )
{
return "no apply<>";
}
class A {
public:
template < unsigned >
int apply( double d )
{
return d + 10.;
}
};
class B {};
int main()
{
A a;
std::cout << f( a ) << std::endl;
B b;
std::cout << f( b ) << std::endl;
}
输出是:
15
no apply<>
现在,如果从第一个.template
定义中删除f()
,则输出变为:
no apply<>
no apply<>
这表示class A
的替换失败,因为它没有任何名为apply
的非模板成员。 SFINAE在行动!
答案 1 :(得分:0)
很抱歉发帖作为答案,但评论似乎对我来说是蠢蠢的。
我见过人们评论declval(),但不需要。可以简单地写
而不是T() decltype( *(T*)0->template apply< 0u >( double() ) ) )
有效,因为它只出现在decltype内部,并且实际上并未在运行时进行评估。另一种选择是声明
T T_obj( void );
然后使用
decltype( T_obj().template apply< 0u >( double() ) ) )
作为旁注,我记得读过Stroustrup设计这种语言,因为他不想用错误的工具做一份工作。 C ++不是元编程的错误语言吗?
虽然c ++ 0x改进了很多东西,但这似乎不是焦点。有没有像c ++那样“接近金属”的其他语言,它提供了更好的工具来编写将在编译时生成代码的元代码?