检查 class 是否有具有给定名称但任何签名的方法
Check if a class has a method with a given name but any signature
我正在尝试找到一种方法来使用 c++11 功能简单地检查给定名称的方法是否存在于 c++ class 中,但没有(!)检查签名。
没有签名检查我什么都找不到,所以我尝试使用here中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");
我认为问题的根源在于我的 approx_equal
在派生的 class(es) 中也使用了基础 class 引用,并且不再与签名匹配。
最后,我想构造一个模板比较函数,如果存在则调用 approx_equal
,或者调用其他函数(例如 ==
用于字符串等,或 fabs(a-b) <= tolerance
用于浮动和双打)。 approx_equal 函数将依次为每个成员调用模板比较。
我实际上得到了那个部分来处理一个丑陋的解决方法,其中每个 class 和 approx_equal
方法也得到一个成员变量 const static char hasApproxEqualMethod
。然后我检查该变量是否按照建议 存在,但这肯定不是正确的方法。
使用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)>);
你可以这样缩短它:
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>());
最终解决方案 使用 SFINAE,以便在 tring 求值时编译不会中止 &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.");
SFINAE 确保在尝试评估静态断言之前可以获得 false
值。
您的代码中的问题是您检查 class 是否具有接受的方法,作为第一个参数,对相同 class
的对象的常量引用
template <class T>
static std::true_type testSignature(bool (T::*)(const T&, double) const);
// .......................................^...........^ same class
但在 Derived
中,您定义了一个接收不同 class (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
};
一个可能的解决方案是放宽 HasApproxEqualMethod
中的测试以接受不同 classes
的对象
template <class T, class U>
static std::true_type testSignature(bool (T::*)(const U&, double) const);
// now class and argument are different...^...........^
这种方式也满足
static_assert(HasApproxEqualMethod<Derived>().value == true, "fail Derived");
如果你想完全避免签名检查,你可以尝试类似的方法
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) );
但是,这样,HasApproxEqualMethod<T>
也是 std::true_type
当 T
有一个带有完全不同签名的 approx_equal
方法时,或者当 approx_equal
是一个简单的成员(一个变量)。
check if a method of a given name exists in a c++ class using c++11 features, but without(!) checking the signature.
[..]
In the end, I want to construct a template comparison function that calls approx_equal if it exists
事实上,您想知道 bool(lhs.approx_equal(rhs, some_double))
是否有效,所以不是确切的签名,而是“兼容”的签名。并且它用于沿着重载调度。
因此,您可以使用 decltype
并订购您的重载:
// helper function to prioritize overload. bigger has more priority
template <std::size_t N> struct overload_priority : overload_priority<N - 1> {};
template <> struct overload_priority<0> {}; // Lowest priority
// member function
template <typename T>
auto generic_approx_equal_impl(const T& lhs, const T& rhs, double tolerance, overload_priority<2>)
-> decltype(bool(lhs.approx_equal(rhs, tolerance))
{
return lhs.approx_equal(rhs, tolerance);
}
// free function
template <typename T>
auto generic_approx_equal_impl(const T& lhs, const T& rhs, double tolerance, overload_priority<2>)
-> decltype(bool(approx_equal(lhs, rhs, tolerance))
{
return approx_equal(lhs, rhs, tolerance);
}
// fallback
template <typename T>
bool generic_approx_equal_impl(const T& lhs, const T& rhs, double tolerance, overload_priority<0>)
{
/*..*/
//return abs(lhs - rhs) < tolerance;
return false;
}
template <typename T>
bool generic_approx_equal(const T& lhs, const T& rhs, double tolerance)
{
// Call with number greater or equal to max overloads
return generic_approx_equal_impl(lhs, rhs, tolerance, overload_priority<10>{});
}
我正在尝试找到一种方法来使用 c++11 功能简单地检查给定名称的方法是否存在于 c++ class 中,但没有(!)检查签名。
没有签名检查我什么都找不到,所以我尝试使用here中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");
我认为问题的根源在于我的 approx_equal
在派生的 class(es) 中也使用了基础 class 引用,并且不再与签名匹配。
最后,我想构造一个模板比较函数,如果存在则调用 approx_equal
,或者调用其他函数(例如 ==
用于字符串等,或 fabs(a-b) <= tolerance
用于浮动和双打)。 approx_equal 函数将依次为每个成员调用模板比较。
我实际上得到了那个部分来处理一个丑陋的解决方法,其中每个 class 和 approx_equal
方法也得到一个成员变量 const static char hasApproxEqualMethod
。然后我检查该变量是否按照建议
使用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)>);
你可以这样缩短它:
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>());
最终解决方案 使用 SFINAE,以便在 tring 求值时编译不会中止 &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.");
SFINAE 确保在尝试评估静态断言之前可以获得 false
值。
您的代码中的问题是您检查 class 是否具有接受的方法,作为第一个参数,对相同 class
的对象的常量引用template <class T>
static std::true_type testSignature(bool (T::*)(const T&, double) const);
// .......................................^...........^ same class
但在 Derived
中,您定义了一个接收不同 class (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
};
一个可能的解决方案是放宽 HasApproxEqualMethod
中的测试以接受不同 classes
template <class T, class U>
static std::true_type testSignature(bool (T::*)(const U&, double) const);
// now class and argument are different...^...........^
这种方式也满足
static_assert(HasApproxEqualMethod<Derived>().value == true, "fail Derived");
如果你想完全避免签名检查,你可以尝试类似的方法
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) );
但是,这样,HasApproxEqualMethod<T>
也是 std::true_type
当 T
有一个带有完全不同签名的 approx_equal
方法时,或者当 approx_equal
是一个简单的成员(一个变量)。
check if a method of a given name exists in a c++ class using c++11 features, but without(!) checking the signature.
[..]
In the end, I want to construct a template comparison function that calls approx_equal if it exists
事实上,您想知道 bool(lhs.approx_equal(rhs, some_double))
是否有效,所以不是确切的签名,而是“兼容”的签名。并且它用于沿着重载调度。
因此,您可以使用 decltype
并订购您的重载:
// helper function to prioritize overload. bigger has more priority
template <std::size_t N> struct overload_priority : overload_priority<N - 1> {};
template <> struct overload_priority<0> {}; // Lowest priority
// member function
template <typename T>
auto generic_approx_equal_impl(const T& lhs, const T& rhs, double tolerance, overload_priority<2>)
-> decltype(bool(lhs.approx_equal(rhs, tolerance))
{
return lhs.approx_equal(rhs, tolerance);
}
// free function
template <typename T>
auto generic_approx_equal_impl(const T& lhs, const T& rhs, double tolerance, overload_priority<2>)
-> decltype(bool(approx_equal(lhs, rhs, tolerance))
{
return approx_equal(lhs, rhs, tolerance);
}
// fallback
template <typename T>
bool generic_approx_equal_impl(const T& lhs, const T& rhs, double tolerance, overload_priority<0>)
{
/*..*/
//return abs(lhs - rhs) < tolerance;
return false;
}
template <typename T>
bool generic_approx_equal(const T& lhs, const T& rhs, double tolerance)
{
// Call with number greater or equal to max overloads
return generic_approx_equal_impl(lhs, rhs, tolerance, overload_priority<10>{});
}