我正在使用SFINAE惯用法来检查某个类型是否有定义了给定签名的方法(some_method()
):
template <typename... Other>
struct has_method {
static constexpr bool value = has_method<Other...>::value;
};
template <typename U, typename... Other>
struct has_method<U, Other...> {
static constexpr bool value =
has_method<U>::value && has_method<Other...>::value;
};
template <typename U>
struct has_method<U> {
template <typename T, T>
struct helper;
template <typename T>
static std::uint8_t check(helper<int (*)(size_t), &T::some_method>*);
template <typename T>
static std::uint16_t check(...);
static constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t);
};
我想修改它以使用std::is_arithmetic
检查函数的返回类型是否为算术类型。这可能吗?
我尝试使用static_assert()
和std::is_arithmetic< decltype(((T*)nullptr)->foo())>::value
一起修改它,但没有成功。
答案 0 :(得分:1)
我认为这就是你要找的东西:
struct Unknown {};
template <typename T_>
struct Wrap
{
typedef T_ type;
};
template <class C_, typename... A_>
struct HasMethodArithmeticReturn
{
template <class U_>
static Wrap<decltype(std::declval<U_>().NAME(std::declval<A_>()...))> test(void*);
template <class U_>
static Unknown test(...);
typedef char yes[1];
typedef char no[2];
template <typename>
struct helper
{
typedef no type;
};
template <typename R_>
struct helper<Wrap<R_>>
{
template <class U_, R_(U_::*)(A_...)>
struct assist
{
typedef yes type;
};
template <class U_>
static typename std::enable_if<std::is_arithmetic<R_>::value, typename assist<U_, &U_::NAME>::type>::type& test(void*);
template <class U_>
static no& test(...);
typedef decltype(test<C_>(0)) type;
};
static const bool value = sizeof(yes) == sizeof(typename helper<decltype(test<C_>(0))>::type);
};
您可以看到它使用live。如果类具有带A_...
参数和算术返回类型的方法,则value
为真。否则,这是错误的。
答案 1 :(得分:1)
除了@James Root的答案,它允许用户指定参数,但不允许用户在OP中同时测试多个类,您还可以执行以下操作:
#include <type_traits>
template <typename... Other>
struct has_method {
static constexpr bool value = has_method<Other...>::value;
};
template <typename U, typename... Other>
struct has_method<U, Other...> {
static constexpr bool value =
has_method<U>::value && has_method<Other...>::value;
};
template <typename U>
struct has_method<U> {
template <typename T>
static auto typeget(int) -> decltype(T::some_method(size_t{}));
template <typename T>
static void typeget(...);
static constexpr bool value =
std::is_arithmetic<decltype(has_method::typeget<U>(0))>::value;
};
实现语法更简单,但您需要以其他方式付费:您必须为希望提供给函数的每组参数类型创建一个新模板,并检测是否可以使用参数,而不是函数是否具有确切的期望参数。
Live on Coliru,虽然我已将size_t
替换为uint8_t
,以证明所需类型可以隐式转换为查询类型的成员函数中的参数类型,然后表达式评估为真。
答案 2 :(得分:1)
编辑:我已更新答案以允许给定方法采取任意参数,而不仅仅是void
签名
我认为这会解决问题:
namespace details {
template<typename...>
struct voider
{
using type=void;
};
template<typename T>
struct GetReturnType;
// non member function
template<typename Ret, typename... Args>
struct GetReturnType<Ret(*)(Args...)>
{
using type = Ret;
};
// mmeber function
template<typename Ret, typename C, typename... Args>
struct GetReturnType<Ret(C::*)(Args...)>
{
using type = Ret;
};
}
template<typename...Ts>
using void_t = typename details::voider<Ts...>::type;
template<typename T, typename=void>
struct has_arithmetic_method : std::false_type{};
template<typename T>
struct has_arithmetic_method<T, void_t<decltype(&T::some_method)>> : std::is_arithmetic<typename details::GetReturnType<decltype(&T::some_method)>::type>::type{};
我使用了voider概念来确保some_method
上的T
访问格式正确,然后从std::is_arithmetic
派生,如果是的话。我必须添加一个帮助器来拉出任意函数的返回类型。
一些结构要进行测试。请注意应该传递的内容如何为some_method
提供不同的参数:
struct FailSomeMethod // lacks a some_method altogether
{
void some_other_method()
{
std::cout << "FailSomeMethod::some_other_method" << std::endl;
}
};
struct PassSomeMethod // has a some_method, but it's not arithmetic
{
void some_method()
{
std::cout << "PassSomeMethod::some_method" << std::endl;
}
};
struct PassArithmeticMethod
{
int some_method(int _a)
{
std::cout << "PassArithmeticMethod::some_method" << std::endl;
return 1;
}
};
struct PassArithmeticMethod2
{
double some_method()
{
return 1.0;
}
};
struct PassArithmeticMethod3
{
float some_method(int a, double b, float c, char d, bool e)
{
return 1.;
}
};
实际测试:
int main()
{
//test the has_arithmetic_method concept
std::cout << "Struct PassArithmeticMethod: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod>::value << std::endl;
std::cout << "Struct PassSomeMethod: " << std::boolalpha << has_arithmetic_method<PassSomeMethod>::value << std::endl;
std::cout << "Struct FailSomeMethod: " << std::boolalpha << has_arithmetic_method<FailSomeMethod>::value << std::endl;
std::cout << "Struct PassArithmeticMethod2: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod2>::value << std::endl;
std::cout << "Struct PassArithmeticMethod3: " << std::boolalpha << has_arithmetic_method<PassArithmeticMethod3>::value << std::endl;
}
输出:
Struct PassArithmeticMethod:true
Struct PassSomeMethod:false
Struct FailSomeMethod:false
Struct PassArithmeticMethod2:true
Struct PassArithmeticMethod3:true
老实说,尽管我的回答(误导性)很长,但我觉得这是最简单的。 voider概念和GetReturnType
助手是通用结构,可以很容易地重复使用。我的代码中没有使用任何constexpr
;我将所有工作留给了true
和false
继承技巧。