目标:实现任何可调用的特性,返回其对称性、返回类型和参数类型。由于指向数据成员的指针也是可调用的,因此应该对这些指针进行处理(并将其视为0-偶然性)。
代码如下,试试这里。这是v2,在收到这里评论之后。除了建议的更改之外,我还添加了对函数引用的支持,现在还提供了有关可调用类的信息(如果是成员函数或数据成员),如果您想要获得不可调用的特性,则提供更好的错误消息。我决定不把引用作为第一个参数类型放到类中,而是单独提供它。测试class_type是否无效将告诉您是否需要对类实例进行引用。实际上,对于指向静态成员函数的指针,class_type是无效的。
#pragma once
#include <cstddef>
#include <tuple>
#include <type_traits>
// derived and extended from https://github.com/kennytm/utils/blob/master/traits.hpp
// and https://stackoverflow.com/a/28213747
// This does not handle overloaded functions (which includes functors with
// overloaded operator()), because the compiler would not be able to resolve
// the overload without knowing the argument types and the cv- and noexcept-
// qualifications. If you do know those already and can thus specify the
// overload to the compiler, you do not need this class. The only remaining
// piece of information is the result type, which you can get with
// std::invoke_result.
namespace detail
{
template <std::size_t i, typename... Args>
struct invocable_traits_arg_impl
{
static_assert(i<sizeof...(Args), "Requested argument type out of bounds (function does not declare this many arguments)");
using type = std::tuple_element_t<i, std::tuple<Args...>>;
};
template <typename R, typename C, bool IsVariadic, typename... Args>
struct invocable_traits_class
{
static constexpr std::size_t arity = sizeof...(Args);
static constexpr auto is_variadic = IsVariadic;
using result_type = R;
using class_type = C;
template <std::size_t i>
using arg = invocable_traits_arg_impl<i,Args...>::type;
};
template <typename R, bool IsVariadic, typename... Args>
struct invocable_traits_free : public invocable_traits_class<R, void, IsVariadic, Args...> {};
}
template <typename T>
struct invocable_traits;
#define INVOCABLE_TRAITS_SPEC(cv,...) \
/* handle functions, with all possible iterations of reference and noexcept */ \
template <typename R, typename... Args> \
struct invocable_traits<R(Args... __VA_OPT__(,) __VA_ARGS__) cv> \
: public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {}; \
template <typename R, typename... Args> \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv &> \
: public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {}; \
template <typename R, typename... Args> \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv &&> \
: public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {}; \
template <typename R, typename... Args> \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv noexcept> \
: public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {}; \
template <typename R, typename... Args> \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv & noexcept> \
: public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {}; \
template <typename R, typename... Args> \
struct invocable_traits<R(Args...__VA_OPT__(,) __VA_ARGS__) cv && noexcept> \
: public detail::invocable_traits_free<R,0 __VA_OPT__(+1),Args...> {}; \
/* handle pointers to member functions (incl. iterations of reference and noexcept) */\
template <typename C, typename R, typename... Args> \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv> \
: public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {}; \
template <typename C, typename R, typename... Args> \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv &> \
: public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {}; \
template <typename C, typename R, typename... Args> \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv &&> \
: public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {}; \
template <typename C, typename R, typename... Args> \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv noexcept> \
: public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {}; \
template <typename C, typename R, typename... Args> \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv & noexcept> \
: public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {}; \
template <typename C, typename R, typename... Args> \
struct invocable_traits<R(C::*)(Args...__VA_OPT__(,) __VA_ARGS__) cv && noexcept> \
: public detail::invocable_traits_class<R,C,0 __VA_OPT__(+1),Args...> {}; \
/* handle pointers to data members */ \
__VA_OPT__( /* no variadic function version for data members, (inverted) skip */ \
template <typename C, typename R> \
struct invocable_traits<R C::* cv> \
: public detail::invocable_traits_class<R,C,false> {}; \
) /* end __VA_OPT___*/
// cover all const and volatile permutations
INVOCABLE_TRAITS_SPEC(, )
INVOCABLE_TRAITS_SPEC(const, )
INVOCABLE_TRAITS_SPEC(volatile, )
INVOCABLE_TRAITS_SPEC(const volatile, )
// and also variadic function versions
INVOCABLE_TRAITS_SPEC(, ...)
INVOCABLE_TRAITS_SPEC(const, ...)
INVOCABLE_TRAITS_SPEC(volatile, ...)
INVOCABLE_TRAITS_SPEC(const volatile, ...)
#undef INVOCABLE_TRAITS_SPEC
// pointers to functions
template <typename R, typename... Args>
struct invocable_traits<R(*)(Args...)> : public invocable_traits<R(Args...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(*)(Args...) noexcept> : public invocable_traits<R(Args...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(*)(Args..., ...)> : public invocable_traits<R(Args..., ...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(*)(Args..., ...) noexcept> : public invocable_traits<R(Args..., ...)> {};
// references to functions
template <typename R, typename... Args>
struct invocable_traits<R(&)(Args...)> : public invocable_traits<R(Args...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(&)(Args...) noexcept> : public invocable_traits<R(Args...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(&)(Args..., ...)> : public invocable_traits<R(Args..., ...)> {};
template <typename R, typename... Args>
struct invocable_traits<R(&)(Args..., ...) noexcept> : public invocable_traits<R(Args..., ...)> {};
// get at operator() of any struct/class defining it (this includes lambdas)
// bit of machinery for better error messages
namespace detail {
template <typename T>
concept HasCallOperator = requires(T)
{
std::declval<T>().operator();
};
template <typename T, bool isCallable>
struct invocable_traits_extract : invocable_traits<decltype(&T::operator())> {};
template <typename T>
struct invocable_traits_extract<T, false>
{
static_assert(std::is_class_v<T>, "passed type is not a class, and thus cannot have an operator()");
static_assert(!std::is_class_v<T> || HasCallOperator<T>, "passed type is a class that doesn't have an operator()");
// to reduce excessive compiler error output
static constexpr std::size_t arity = 0;
static constexpr auto is_variadic = false;
using result_type = void;
using class_type = void;
template <size_t i> struct arg { using type = void; };
};
}
template <typename T>
struct invocable_traits : detail::invocable_traits_extract<std::decay_t<T>, detail::HasCallOperator<T>> {};测试代码:
#include <string>
void test(int)
{}
void test2(int) noexcept
{}
void testEllipsis(int,...)
{}
struct tester
{
void yolo(char)
{}
void yoloEllipsis(char, ...)
{}
static long yoloStatic(short)
{}
void operator()(int in_)
{}
std::string field;
};
int main()
{
auto lamb = [](const int& in_) {return "ret"; };
using traits = invocable_traits<decltype(lamb)>;
static_assert(std::is_same_v<const char*, traits::result_type>, "");
static_assert(std::is_same_v<const int&, traits::arg<0>>, "");
using traits2 = invocable_traits<decltype(&test)>;
static_assert(std::is_same_v<void, traits2::result_type>, "");
static_assert(std::is_same_v<int, traits2::arg<0>>, "");
using traits2b = invocable_traits<decltype(&test2)>;
static_assert(std::is_same_v<void, traits2b::result_type>, "");
static_assert(std::is_same_v<int, traits2b::arg<0>>, "");
static_assert(!traits2b::is_variadic, "");
using traits2c = invocable_traits<decltype(&testEllipsis)>;
static_assert(std::is_same_v<void, traits2c::result_type>, "");
static_assert(std::is_same_v<int, traits2c::arg<0>>, "");
static_assert(traits2c::is_variadic, "");
using traits3 = invocable_traits<decltype(&tester::yolo)>;
static_assert(std::is_same_v<void, traits3::result_type>, "");
static_assert(std::is_same_v<char, traits3::arg<0>>, "");
using traits3b = invocable_traits<decltype(&tester::yoloEllipsis)>;
static_assert(std::is_same_v<void, traits3b::result_type>, "");
static_assert(std::is_same_v<tester, traits3b::class_type>, "");
static_assert(std::is_same_v<char, traits3b::arg<0>>, "");
static_assert(traits3b::is_variadic, "");
using traits3c = invocable_traits<decltype(&tester::yoloStatic)>;
static_assert(std::is_same_v<long, traits3c::result_type>, "");
static_assert(std::is_same_v<void, traits3c::class_type>, "");
static_assert(std::is_same_v<short, traits3c::arg<0>>, "");
static_assert(!traits3c::is_variadic, "");
using traits4 = invocable_traits<decltype(&tester::field)>;
static_assert(std::is_same_v<std::string, traits4::result_type>, "");
static_assert(traits4::arity==0, "");
using traits5 = invocable_traits<std::add_rvalue_reference_t<decltype(lamb)>>;
static_assert(std::is_same_v<const char*, traits5::result_type>, "");
static_assert(std::is_same_v<const int&, traits5::arg<0>>, "");
using traits6 = invocable_traits<std::add_lvalue_reference_t<decltype(lamb)>>;
static_assert(std::is_same_v<const char*, traits6::result_type>, "");
static_assert(std::is_same_v<decltype(lamb), traits6::class_type>, "");
static_assert(std::is_same_v<const int&, traits6::arg<0>>, "");
auto lamb2 = [](const int& in_, ...) mutable noexcept {return "ret"; };
// functor
using traits7 = invocable_traits<tester>;
static_assert(std::is_same_v<void, traits7::result_type>, "");
static_assert(std::is_same_v<int, traits7::arg<0>>, "");
using traits8 = invocable_traits<decltype(lamb2)>;
static_assert(std::is_same_v<const char*, traits8::result_type>, "");
static_assert(std::is_same_v<const int&, traits8::arg<0>>, "");
static_assert(traits8::is_variadic, "");
/*using traits9 = invocable_traits<int>;
static_assert(std::is_same_v<const char*, traits9::result_type>, "");*/
return 0;
}发布于 2022-01-05 22:40:12
一定要在宏使用后对其进行#undef。这是为了在标题中展开,而不是用户调用。
它对重载的函数有什么作用?
https://codereview.stackexchange.com/questions/272668
复制相似问题