这算是SFINAE吗?

10

大约一周前,我提出了一个问题,询问如何只有在类型具有特定成员函数时才能简单地实例化类模板。在我的答案中,我得到了一个有点复杂的解决方案。但后来我尝试自己做。我只想知道是否足够确定给定类型 T 是否有一个名为 f、参数为 0 的 void 函数。

#include <type_traits>
#include <utility>

template <typename T, typename = void>
struct has_f : std::false_type { };

template <typename T>
struct has_f<
    T,
    decltype(std::declval<T>().f(), void())> : std::true_type { };

template <typename T, typename = typename std::enable_if<has_f<T>::value>::type>
struct A { };

struct B
{
    void f();
};

struct C { };

template class A<B>; // compiles
template class A<C>; // error: no type named ‘type’ 
                     // in ‘struct std::enable_if<false, void>’

如果是这样,为什么在这个帖子中,其他答案都那么复杂呢?

2
是的,这就是SFINAE,而且是正确的。至于为什么其他答案更复杂...很难说。 - Andy Prowl
为了澄清,has_f 本身依赖于 SFINAE,而 A 则不依赖。 - Luc Danton
作为一种可能性,某些编译器(咳咳 MSVC2012)中的SFINAE支持有点糟糕,因此其中一些复杂性可能是为了具有更可移植的SFINAE而进行的解决方法。另外,由于rvalue引用到this特性,在C++1y中,std::declval<T&>().f()std::declval<T>().f()(或者是std::declval<T&&>().f()?)可能是不同的东西。 - Yakk - Adam Nevraumont
1个回答

7

是的,您已经以C++11 SFINAE最简单、最惯用的方式解决了它。

请注意,您没有检查返回类型是否为void,是否为非静态成员,也没有检查是否有参数。f只需调用无参数即可。它甚至可以是一个函数对象。

要检查返回void的零元非静态成员函数,请使用:

template <typename T>
struct has_f<T, decltype(void( static_cast< void (T::*)( void ) >( &T::f ) )) >
    : std::true_type {};

template <typename T>
struct has_f<T, decltype(void( static_cast< void (T::*)( void ) const >( &T::f ) )) >
    : std::true_type {};

我可以使用 std::result_of 来检查返回类型是否为 void 吗? - Me myself and I
@MemyselfandI std::result_of并不要求符合SFINAE。然而,最近的实现确实尝试使其符合SFINAE(否则该特性使用起来会很麻烦)。 - Luc Danton
@LucDanton,这不是C++11草案所要求的,但它在不久之后作为C++1y功能添加进来了。无论如何,除非您正在使用INVOKE的特殊成员指针情况,否则它并不能真正取代decltype - Potatoswatter
f 被重载时,使用 decltype(&T::f) 会失败。但我相信问题中的代码已经正确处理了这个问题。 - Ben Voigt
@BenVoigt,需要使用static_cast。这很棘手。 - Potatoswatter
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接