更新:这段代码有新版本:v2在这里发布、v3在这里发布和v4在这里发布
目标:实现任何可调用的特性,返回其对称性、返回类型和参数类型。由于指向数据成员的指针也是可调用的,因此应该对这些指针进行处理(并将其视为0-偶然性)。
代码如下,试试这里。我有以下问题:
template <typename T> struct callable_traits : callable_traits<decltype(&std::decay_t<T>::operator())>上出现的,但我没有设法测试是否存在一个操作符(),这样就可以发出static_assert。useInFunc()中函数的使用情况。在MSVC上,我需要做traits::template arg<0>而不是traits::arg<0> (在MSVC上不需要输入名称,但是GCC要求这样做,这就是为什么会出现这种情况)。添加template有点困难(因为它是一个依赖类型?)。有什么办法可以避免吗?一般情况下,我想知道为什么我需要添加typename时,在函数内使用,而不是主要用于GCC,以及为什么template为MSVC.#pragma once
// derived and extended from https://github.com/kennytm/utils/blob/master/traits.hpp
// and https://stackoverflow.com/a/28213747
#include <tuple>
#include <type_traits>
// get at operator() of any struct/class defining it (this includes lambdas)
template <typename T>
struct callable_traits : callable_traits<decltype(&std::decay_t<T>::operator())>
{};
#define SPEC(cv,unused) \
/* handle anything with a function-like signature */ \
template <typename R, typename... Args> \
struct callable_traits<R(Args...) cv> \
{ \
using arity = std::integral_constant<std::size_t, sizeof...(Args) >; \
\
using result_type = R; \
\
template <std::size_t i> \
using arg = typename std::tuple_element<i, std::tuple<Args...,void>>::type; \
}; \
/* handle pointers to data members */ \
template <typename C, typename R> \
struct callable_traits<R C::* cv> \
{ \
using arity = std::integral_constant<std::size_t, 0 >; \
\
using result_type = R; \
\
template <std::size_t i> \
using arg = typename std::tuple_element<i, std::tuple<void>>::type; \
}; \
/* handle pointers to member functions, all possible iterations of reference and noexcept */ \
template <typename C, typename R, typename... Args> \
struct callable_traits<R(C::*)(Args...) cv> : public callable_traits<R(Args...)> {};\
template <typename C, typename R, typename... Args> \
struct callable_traits<R(C::*)(Args...) cv &> : public callable_traits<R(Args...)> {};\
template <typename C, typename R, typename... Args> \
struct callable_traits<R(C::*)(Args...) cv &&> : public callable_traits<R(Args...)> {};\
template <typename C, typename R, typename... Args> \
struct callable_traits<R(C::*)(Args...) cv noexcept> : public callable_traits<R(Args...)> {};\
template <typename C, typename R, typename... Args> \
struct callable_traits<R(C::*)(Args...) cv & noexcept> : public callable_traits<R(Args...)> {};\
template <typename C, typename R, typename... Args> \
struct callable_traits<R(C::*)(Args...) cv && noexcept> : public callable_traits<R(Args...)> {};
// cover all const and volatile permutations
SPEC(, )
SPEC(const, )
SPEC(volatile, )
SPEC(const volatile, )
// handle pointers to free functions
template <typename R, typename... Args>
struct callable_traits<R(*)(Args...)> : public callable_traits<R(Args...)> {};测试代码:
void test(int)
{
}
template <typename Func>
void useInFunc(Func)
{
using traits = callable_traits<Func>;
static_assert(std::is_same_v<const char*, typename traits::result_type>, "");
static_assert(std::is_same_v<const int&, typename traits::template arg<0>>, "");
}
struct tester
{
void yolo(char)
{
}
std::string field;
};
int main(int argc, char **argv)
{
auto lamb = [](const int& in_) noexcept {return "ret"; };
using traits = callable_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>>, "");
useInFunc(lamb);
using traits2 = callable_traits<decltype(&test)>;
static_assert(std::is_same_v<void, traits2::result_type>, "");
static_assert(std::is_same_v<int, traits2::arg<0>>, "");
using traits3 = callable_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 traits4 = callable_traits<decltype(&tester::field)>;
static_assert(std::is_same_v<std::string, traits4::result_type>, "");
static_assert(std::is_same_v<void, traits4::arg<0>>, "");
using traits5 = callable_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 = callable_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<const int&, traits6::arg<0>>, "");
/*using traits7 = callable_traits<int>;
static_assert(std::is_same_v<const char*, traits7::result_type>, "");
static_assert(std::is_same_v<const int&, traits7::arg<0>>, "");*/
return 0;
}发布于 2022-01-05 01:45:44
的支持
如果您试图获得独立函数(即noexcept )的可调用特性,则无法编译。这很容易通过添加以下内容来解决:
template <typename R, typename... Args> \
struct callable_traits<R(Args...) cv noexcept>: public callable_traits<R(Args...) cv> {}; \正如Jarod42所提到的,您的代码也不处理各种函数。在代码中引用的StackOverflow帖子中,这个答案展示了一种添加对此的支持的方法。
中添加void
不应该需要将void附加到您希望获得元素类型的元组中。只需写:
template <std::size_t i> \
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; \调用方应使用arity检查函数是否接受任何参数。
我不明白为什么会有与数据成员的指针匹配的重载。它们是不可调用的,所以我会删除模板重载。
arity成为值与其将arity作为一种类型,不如考虑让它成为一个值,如下所示:
static constexpr auto arity = sizeof...(Args);这避免了在之后添加::value的需要。
如果你不需要一个宏就能处理函数的简历--那就太好了。还有一些重复处理常规函数和noexcept函数。也许可以通过采用四个阶段的方法来实现:
operator()。noexcept。noexcept函数类型的特征。这可能不容易。特别是,没有std::remove_noexcept,所以您可能不得不使用你自己去实现。
除非您已经知道参数的类型和cv限定,否则您是不可能这样做的。否则,编译器无法解析重载。如果您知道参数,可以使用std::invoke_result获取唯一丢失的信息(即返回类型)。
template和typename看似不必要的需求
实际上,有时编译器无法预先知道标识符是否引用了类型、变量或函数。解析模板定义时,它没有实例化模板。因此,它无法确切地知道std::tuple_element<i, std::tuple<Args...>>::type将是什么。您可以通过在前面添加typename来消除这种歧义。类似地,如果编译器无法推断这一点,则可能需要在某些地方添加template,但在本例中使用的MSVC版本似乎存在缺陷。
https://codereview.stackexchange.com/questions/272633
复制相似问题