检查一个类是否有一个具有给定名称但有任何签名的方法

Han*_*nno 4 c++ methods templates sfinae c++11

我试图找到一种方法来简单地检查给定名称的方法是否存在于使用 c++11 特性的 c++ 类中,但没有(!)检查签名。

如果没有签名检查,我无法找到任何东西,所以我尝试从这里使用 Valentin Milea 的解决方案并对其进行修改(见下文),但我对 C++ 的理解不够深入,无法真正掌握那里发生的事情:

#include <type_traits>

template <class C>
class HasApproxEqualMethod
{
    template <class T>
    static std::true_type testSignature(bool (T::*)(const T&, double) const);

    template <class T>
    static decltype(testSignature(&T::approx_equal)) test(std::nullptr_t);

    template <class T>
    static std::false_type test(...);

public:
    using type = decltype(test<C>(nullptr));
    static const bool value = type::value;
};

class Base {
public:
virtual ~Base();
virtual bool approx_equal(const Base& other, double tolerance) const;
};

class Derived : public Base {
public:
     // same interface as base class
    bool approx_equal(const Base& other, double tolerance) const;
};

class Other {};

static_assert(HasApproxEqualMethod<Base>().value == true, "fail Base");
static_assert(HasApproxEqualMethod<Other>().value == false, "fail Other");
 // this one fails:
static_assert(HasApproxEqualMethod<Derived>().value == true, "fail Derived");
Run Code Online (Sandbox Code Playgroud)

我认为问题的根源在于我approx_equal在派生类中也使用了基类引用,并且不再与签名匹配。

最后,我想构造一个模板比较函数,approx_equal如果它存在就调用它,或者其他东西(例如==字符串等,或fabs(a-b) <= tolerance浮点数和双精度数)。然后 approx_equal 函数将依次调用每个成员的模板比较。

我实际上让那部分使用了一个丑陋的解决方法,其中每个带有approx_equal方法的类也有一个成员变量const static char hasApproxEqualMethod。然后我检查该变量是否按照此处的建议存在,但这肯定不是要走的路。

tma*_*ric 5

使用怎么样std::is_member_function_pointer_v(需要 c++17):

// Works
static_assert(std::is_member_function_pointer_v<decltype(&Base::approx_equal)>); 
// Works
static_assert(std::is_member_function_pointer_v<decltype(&Derived::approx_equal)>); 
// Fails as expected
static_assert(!std::is_member_function_pointer_v<decltype(&Other::approx_equal)>); 
Run Code Online (Sandbox Code Playgroud)

你可以像这样缩短它:

template<typename Class> 
constexpr bool has_approx_equal()
{
    return std::is_member_function_pointer_v<decltype(&Class::approx_equal)>; 
}

static_assert(has_approx_equal<Base>()); 
static_assert(has_approx_equal<Derived>()); 
static_assert(!has_approx_equal<Other>());
Run Code Online (Sandbox Code Playgroud)

最终解决方案使用 SFINAE 以便在尝试评估时编译不会中止&Other::approx_equal

template<typename Class, typename Enabled = void> 
struct has_approx_equal_s
{
    static constexpr bool value = false;  
};

template<typename Class> 
struct has_approx_equal_s
<
    Class, 
    std::enable_if_t
    <
        std::is_member_function_pointer_v<decltype(&Class::approx_equal)>
    > 
> 
{
    static constexpr bool value = std::is_member_function_pointer_v<decltype(&Class::approx_equal)>; 
};

template<typename Class> 
constexpr bool has_approx_equal()
{
    return has_approx_equal_s<Class>::value; 
};

static_assert(has_approx_equal<Base>()); 
static_assert(has_approx_equal<Derived>()); 
static_assert(has_approx_equal<Other>(), "Other doesn't have approx_equal.");
Run Code Online (Sandbox Code Playgroud)

SFINAE 确保false在尝试评估静态断言之前可以获得价值。


max*_*x66 2

代码中的问题是您检查一个类是否具有接受对同一类的对象的常量引用作为第一个参数的方法

template <class T>
static std::true_type testSignature(bool (T::*)(const T&, double) const);
// .......................................^...........^   same class
Run Code Online (Sandbox Code Playgroud)

但在内部Derived定义了一个接收不同类对象的方法 ( Base)

// ...VVVVVVV  object is Derived
class Derived : public Base {
public:
     // same interface as base class
    bool approx_equal(const Base& other, double tolerance) const;
    // .....................^^^^  method accept Base
};
Run Code Online (Sandbox Code Playgroud)

一个可能的解决方案是放宽测试HasApproxEqualMethod以接受不同类的对象

template <class T, class U>
static std::true_type testSignature(bool (T::*)(const U&, double) const);
// now class and argument are different...^...........^
Run Code Online (Sandbox Code Playgroud)

这样也满意

static_assert(HasApproxEqualMethod<Derived>().value == true, "fail Derived");
Run Code Online (Sandbox Code Playgroud)

如果你想完全避免签名检查,你可以尝试类似的方法

template <typename T>
constexpr auto haemHelper (T const &, int)
   -> decltype( &T::approx_equal, std::true_type{} );

template <typename T>
constexpr std::false_type haemHelper (T const &, long);

template <typename T>
using HasApproxEqualMethod = decltype( haemHelper(std::declval<T>(), 0) );
Run Code Online (Sandbox Code Playgroud)

但是,这种方式也HasApproxEqualMethod<T>适用于具有完全不同签名的方法或简单成员(变量)的情况。std::true_typeTapprox_equalapprox_equal