【问题标题】:How to detect the presence of a static member function with certain signature?如何检测具有特定签名的静态成员函数的存在?
【发布时间】:2014-04-17 12:53:33
【问题描述】:

我发现了几个关于在编译时(通过 SFINAE)检测给定类是否具有特定名称、类型或签名的成员的 SO 问题和答案。但是,我找不到一个也适用于 static public 成员函数的函数(当指向成员的技巧不起作用时)。有什么想法吗?

【问题讨论】:

  • 你想用私有方法做什么?我假设你想要一个给定的签名?
  • 是的,我知道,这是个问题。 class foo { private: static bool bar(); }; 你是不是想说'foo 有一个static 方法bar'?
  • @Yakk 好的,现在我明白了。不,我不想要非公共静态成员。我编辑了问题以反映这一点(从这个意义上说,答案已经是正确的:他们没有检测到非公共成员)。

标签: c++ templates c++11 template-meta-programming sfinae


【解决方案1】:

以下可能会有所帮助:(https://ideone.com/nDlFUE)

#include <cstdint>

#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)               \
    template <typename U>                                                   \
    class traitsName                                                        \
    {                                                                       \
    private:                                                                \
        template<typename T, T> struct helper;                              \
        template<typename T>                                                \
        static std::uint8_t check(helper<signature, &funcName>*);           \
        template<typename T> static std::uint16_t check(...);               \
    public:                                                                 \
        static                                                              \
        constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
    }

然后定义一个特征:

DEFINE_HAS_SIGNATURE(has_foo, T::foo, void (*)(void));

【讨论】:

  • 当我尝试将它与 gcc 4.8.4 一起使用时,我得到:error: declaration of ‘class T’ template&lt;typename T, T&gt; struct helper;
  • @DanielMoodie:使用4.8.14.8.5 按预期工作。 (不能用 4.8.4 测试)。
【解决方案2】:

这是一种方法:

#include <type_traits>

template<typename, typename>
struct has_fun;

template<typename T, typename Ret, typename... Args>
struct has_fun<T, Ret(Args...)> {
    template<typename U, U> struct Check;

    template<typename U>
    static std::true_type Test(Check<Ret(*)(Args...), &U::fun>*);

    template<typename U>
    static std::false_type Test(...);

    static const bool value = decltype(Test<T>(0))::value;
};

它是为一个名为fun 的函数编写的。像has_fun&lt;T, int(int, int)&gt;::value一样使用它。

这是另一个:

#include <type_traits>

template<typename, typename>
struct has_fun;

template<typename T, typename Ret, typename... Args>
struct has_fun<T, Ret(Args...)> {

    struct No {}; // need a unique type for the second overload
                  // so it doesn't match Ret and give a false positive
    template<typename U>
    static auto Test(int) -> decltype( U::fun(std::declval<Args>()...) );

    template<typename U>
    static No Test(...);

    static const bool value =
        std::is_same<decltype(Test<U>(0)), Ret>{};
};

测试函数的返回类型是否可转换为Ret 而不是检查exatch 匹配可能是明智的。在这种情况下使用is_convertible 而不是is_same,同时检查返回类型是否不同于No(如Yakk points out,那里有几乎可以从任何东西构造的类型) .

【讨论】:

  • 不客气。我写的。我还没有测试过,所以可能会有错误。
  • 这实际上只检测公共静态成员,正是我想要的!
  • 是否对争论进行转换但不返回值,为什么 &amp;&amp; 它什么都不做?
  • @Yakk 对,&& 是一种习惯,不需要。返回类型的转换 - 这可能是正确的做法,但取决于 OP。感谢您的提醒。
  • 这确实带来了另一个有趣的角落案例:std::function&lt;whatever&gt; is_convertible 来自您的has_fun&lt;whatever&gt;::No
【解决方案3】:

只需调用成员函数并丢弃 SFINAE 上下文中的结果。如果成功,则该方法存在。如果失败,则方法不会。

// not needed in C++1y
template<class T,class V=void>using enable_if_t=typename enable_if<T,V>::type;

// If the other tests fail, the type T does not have a method `foo` of
// signature Sig.  The class=void parameter is an implementation detail
// that in an industrial quality implementation we would hide in a helper
// template type.
template<class T,class Sig,class=void>struct has_foo:std::false_type{};

// For R(Args...), we attempt to invoke `T::foo` with (Args...), then check
// if we can assign the return value to a variable of type R.
template<class T,class R,class...Args>
struct has_foo<T,R(Args...),
  enable_if_t< // std:: in C++1y
    std::is_convertible<
      decltype( T::foo( std::declval<Args>()... ) ),
      R
    >::value
    && !std::is_same<R, void>::value
  >
>: std::true_type {};
// for `void` return value, we only care if the function can be invoked,
// no convertible test required:
template<class T,class...Args>
struct has_foo<T,void(Args...),
  decltype( void(T::foo( std::declval<Args>()... ) ) )
>: std::true_type {};

使用:

 has_foo< bar, int(int) >::value

它检查int r = T::foo( 7 ) 是否是一个有效的表达式,而不是精确的签名匹配。

【讨论】:

  • 那是一段丑陋的代码。 +1 仍然:)
  • @jrok 电话编码价格。修正了一个或两个错字并通过触摸使其更漂亮。
  • foo 的返回类型为void 时,有一个"ambiguous partial template specialization"(两者都是部分专业化匹配) - 我在里面添加了一个!std::is_same&lt;R, void&gt;::value std::enable_if_t 来纠正这个问题,但我想知道是否有更好的方法?
  • @holt 什么版本的编译器和 C++?上次阅读is_convertible 的C++ 标准定义时,void 无法转换为void。此外,R=void 的专业性应该低于硬编码的void。您的修复应该足够好,我只是好奇编译器需要它。 (我认为is_convertible&lt;void,void&gt; 需要常规的void,这是针对C++20 提出的)。
  • @Yakk 我用 clang 和 gcc 测试过,使用不同的版本,我总是得到相同的结果:godbolt.org/g/BqLtU2,也许我错过了什么......我只尝试了 c++14 (@ 987654336@).
猜你喜欢
  • 2015-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-27
  • 1970-01-01
  • 2013-08-22
  • 2010-10-20
相关资源
最近更新 更多