【问题标题】:SFINAE to determine if a type has a potentially overloaded methodSFINAE 确定类型是否具有潜在的重载方法
【发布时间】:2016-09-28 14:16:56
【问题描述】:

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

我找到了 [this question],它为我的问题提供了一个相当好的答案,但当类型为方法提供重载时它会失败:

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)>{};

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

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, "");
}

Original live demo

如果出现重载,代码会失败:

struct Foo { int xxx() {return 0;}  void xxx(int){} };

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

Failing live demo with overloaded method

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

【问题讨论】:

  • 好吧,你想测试什么?只要名称 xxx 完全存在(为什么?),或者如果您可以使用一些参数(哪些参数?)调用 xxx
  • 我不认为如果函数重载时不提供附加参数是否可行。但我很想看到一个答案:)
  • 我认为这里的问题和答案可能比@iammilind 链接到的问题更全面。也许这应该被标记为骗子,而不是保留这个问题?
  • 这个问题不是重复的,因为在这里 OP 增加了重载方法的可能性,而张贴的作者没有。

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


【解决方案1】:

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

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(),我会做不同的事情。

【讨论】:

  • 真是聪明,当成员确实存在时触发失败...
  • 自从 OP 代码中的最后一次测试如果关于属性 xxx 不是成员函数,我认为这不是 OP 正在寻找的。​​span>
  • @Holt 一旦你知道这个名字存在,确保它的类型是一个函数类型就不用多费力气了。
  • @Holt 添加了那个额外的位。
  • 还需要注意工会。如果T::xxx 不可访问,您会遇到“重载函数”的情况。
【解决方案2】:

正确的鸭子类型检查是“您的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&lt;T&gt; 是真还是假取决于我们是否可以做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-able,我们得到:

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

【讨论】:

  • 注意:can_apply 接近于std::experimental::is_detected
  • 如何解释成员函数是私有的?这将有助于 CRTP 的东西。
  • @rl-s 我不明白这个问题。如果.xxx() 是私有的,你希望它做什么?你觉得它有什么作用?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-25
  • 2021-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多