如何检测一个成员函数有特定的函数,有特定的参数

How to detect that a member function has specific function, taking specific arguments?

本文关键字:函数 参数 一个 何检测 检测 成员      更新时间:2023-10-16

我已经有了SFINAE代码,可以检测给定的成员函数是否存在并且可以接受特定类型的实例。

我试图检测何时有一个特定的成员函数作为它的参数为NON-REFERENCE。

示例代码

template<typename TYPE_T>
class HasSwapMemberImpl
{
    /*
     * This uses the compilers type deduction magic to ensure that there's SOME swap function
     * that's a member of CLASS_T, that can be passed an instance of CLASS_T, in some fashion.
     * This doesn't determine that the argument for the function is a reference.
     */
    template <typename CLASS_T>
    static auto test_compatible_swap_function_exists(CLASS_T * p) -> decltype(p->swap(*static_cast<CLASS_T*>(nullptr)), boost::true_type());
    /*
     * If no substitutions can satisfy p->swap(*static_cast<CLASS_T*>(nullptr)
     * we end up here as fallback.
     */
    template<typename>
    static boost::false_type test_compatible_swap_function_exists(...);
public:
    typedef decltype(test_compatible_swap_function_exists<TYPE_T>(nullptr)) type;
    static const bool value = type::value;
};
/**
 * brief This MetaProgramming helper class determines if there exists a method named "swap" in the ARG_T type, that takes ARG_T as an argument.
 *
 * If ARG_T.swap(ARG_T) is a valid function, then HasSwapMember inherits from boost::true_type. Else it inherits from boost::false_type.
 */
template<typename ARG_T>
struct HasSwapMember : public HasSwapMemberImpl<ARG_T>::type { };

这适用于我写的几十个测试用例,所以我在这里不需要任何帮助。

相反,我要做的是检测这样的情况

struct NonReferenceSwap
{
    void swap(NonReferenceSwap) {}
};
struct ReferenceSwap
{
    void swap(ReferenceSwap &) {}
};

我想要一些元编程助手,MetaHelperType,这样的MetaHelperType继承自boost::true_type,但MetaHelperType导致编译器错误的消息,如"交换函数必须接受引用参数!"

或者,换句话说:

MetaHelperType<ReferenceSwap>; // IS A boost::true_type
MetaHelperType<NoSwapFunction>; // IS A boost::false_type
MetaHelperType<NonReferenceSwap>; // Compiler error

为什么我想要这个?尝试调用foo.swap(bar)是一个无意义的概念,当bar将作为值传递时。我的组织中有很多c++编程新手,我们希望能够立即发现这个错误,而不是发现有人忘记了&2个月后。

我使用的是Visual Studio 2010,所以不是所有的c++11特性都可用。上面的代码可以正常工作,所以至少这些特性可以正常工作。

这可能吗?如果是的话,c++的最低版本是什么?c++ 11, c++ 14, c++ 17?

我使用以下代码进行精确签名检查:

#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_ref_swap, T::swap, void (T::*)(T&));
DEFINE_HAS_SIGNATURE(has_value_swap, T::swap, void (T::*)(T));

然后,你可以写你的助手检查:

template <typename T>
struct MetaHelperType_impl
{
    static_assert(!has_value_swap<T>::value,
                  "Incorrect implementation of T::swap, "
                  "signature should be void T::swap(T&) instead of T::swap(T)");
    using type = std::conditional_t<has_ref_swap<T>::value,
                                    boost::true_type,
                                    boost::false_type>;
};
template <typename T>
using MetaHelperType = typename MetaHelperType_impl<T>::type;
演示

获取swap的成员函数指针,并将其强制转换为您期望的正确签名。与enable_if一起,您可以检查此强制转换是否失败:

template <typename T>
struct swap_signature { typedef void(T::*type)(T&); };
template <typename T, typename = void>
struct is_swappable : std::false_type {};
template <typename T>
struct is_swappable<T,typename std::enable_if<std::is_member_function_pointer<
decltype(static_cast<typename swap_signature<T>::type>(&T::swap))>::value>::type> : std::true_type {};

然后可以测试:

is_swappable<ReferenceSwap>::value // true
is_swappable<NonReferenceSwap>::value // false