SFINAE确定类型是否具有可能过载的方法

时间:2016-09-28 14:16:57

标签: c++ c++11 c++14 template-meta-programming

我正在寻找一个SFINAE解决方案,以便在编译时检查类型是否有方法。我的目标是检查类型是否有效"duck type",但是我想使用End of script output before headers. 来提供信息性消息,而不是无用的编译错误。

我发现[这个问题],它为我的问题提供了一个相当不错的答案,除非它在类型为方法提供重载时失败:

static_assert

这适用于以下示例,并区分方法和成员变量:

template<typename...> // parameter pack here
using void_t = void;

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

template<typename T>
struct has_xxx<T, void_t<decltype(&T::xxx)>> :
  std::is_member_function_pointer<decltype(&T::xxx)>{};

Original live demo

如果出现过载,代码将失败:

struct Foo { int xxx() {return 0;}; };
struct Foo2 {};
struct Foo3{ static double xxx;};
double Foo3::xxx = 42;

int main() {
   static_assert(has_xxx<Foo>::value, "");
   static_assert(!has_xxx<Foo2>::value, "");
   static_assert(!has_xxx<Foo3>::value, "");
}

Failing live demo with overloaded method

如何改进此代码以处理重载?

2 个答案:

答案 0 :(得分:5)

如果您所做的只是检查名称 xxx是否存在于T类型中,那么我们可以采用以下方法,适用于所有非final 1}} / union类型T。我们可以创建一个从T和具有成员xxx的后备类型公开继承的新类型。当且仅当xxx没有成员开头时,我们才能明确地访问派生类型T

template <class T>
class has_xxx_impl
{
private:
    struct Fallback {
        int xxx;
    };

    struct Derived : T, Fallback { };

    template <class U>
    static std::false_type test(decltype(&U::xxx)* );
    template <class U>
    static std::true_type test(...);
public:
    using type = decltype(test<Derived>(nullptr));
};

template <class T>
struct has_xxx : has_xxx_impl<T>::type
{ };

如果您想查看更具体的内容 - 例如T,您可以致电T{}.xxx(),我会采取不同的方式。

答案 1 :(得分:3)

正确的鸭子类型检查是“可以使用特定签名调用您的xxx,并在特定上下文中使用其返回值”。重载xxx没有用,因为它的使用方式很重要。

我们从can_apply

开始
namespace details {
  template<template<class...>class Z, class, class...>
  struct can_apply:std::false_type{};
  template<template<class...>class Z, class...Ts>
  struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:
    std::true_type{};
}

template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;

告诉您是否可以合法地将给定的参数传递给某个模板。

然后我们表达我们想要的鸭子类型:

template<class T>
using xxx_result = decltype( std::declval<T>().xxx() );

template<class T>
using can_xxx = can_apply< xxx_result, T >;

can_xxx<T>是真实的还是假的,这取决于我们是否可以t.xxx()

如果我们想要一个类型限制,我们只需:

template<class T, class R>
using xxx_result_as_R = decltype( R(std::declval<T>().xxx()) );
template<class T, class R>
using can_xxx_as_R = can_apply< xxx_result_as_R, T, R >;

因此,如果您希望xxx返回int - 能够,我们会:

template<class T>
using valid_xxx = can_xxx_as_R<T, int>;