转发模板自动

问题描述 投票:7回答:1

上下文

(1)可以使用以下特征提取可调用的返回类型和参数类型:

#include <tuple>

template<class T>
struct callable_trait
{};

template<class R, class... Args>
struct callable_trait<R(Args...)>
{
    using return_type    = R;
    using argument_types = std::tuple<Args...>;
};

(2)从C ++ 17开始,可以使用template<auto>定义模板:

如果模板参数声明为auto,则其类型将从相应的参数中推导出来。

(3)然后我应该能够提供一些语法糖:

template<auto callable>
using return_type = typename callable_trait<decltype(callable)>::return_type;

......但是效果不好......

void f();
void g(return_type<f>);
error: no type named 'return_type' in 'callable_trait<void (*)()>'
using return_type = typename callable_trait<decltype(callable)>::return_type;
^~~~~

一个lambda没有帮助......

auto lambda= [](){};
void h(return_type<lambda>);
error: a non-type template parameter cannot have type '(lambda at <source>:19:14)'
void h(return_type<lambda>);
                   ^

Live demo

我怎么能绕过这个呢?

c++ templates c++17 typetraits
1个回答
10
投票

在函数的情况下,这里的问题是函数的decltype(callable)返回一个函数指针,它与你的特化不匹配。使用lambda,你得到了lambda的类型,而不是它的operator()。如果您使用成员函数也会遇到同样的问题,因为您的特化与成员函数指针不匹配。

你需要的东西可以采取所有这些类型,并给你一个R(Args...)作为回报。值得庆幸的是,我们有std::function,它只是为了做这件事。它有扣除指南,允许它采取任何功能类型,并使std::function<R(Args...)>匹配其签名。使用std::function您的代码可以成为

template<class T>
struct callable_trait
{};

template<class R, class... Args>
struct callable_trait<std::function<R(Args...)>>
{
    using return_type    = R;
    using argument_types = std::tuple<Args...>;
    static constexpr size_t argument_count = sizeof...(Args);
};

template<auto callable>
using return_type = typename callable_trait<decltype(std::function{callable})>::return_type;

template<auto callable>
static constexpr size_t argument_count = callable_trait<decltype(std::function{callable})>::argument_count;

void f();
void g(return_type<f>);

auto lambda = [](){};
void h(return_type<lambda>);

void e(int, int, int);
static_assert(argument_count<e> == 3, "oh no");

但这只适用于gcc head。 Clang无法推断出std::function和早期版本的gcc和MSVS失败的原因如下:Why is gcc failing when using lambda for non-type template parameter?

如果切换到使用类型参数并使用decltype可以同时使用gcc和MSVS但是clang仍然有扣除指南的问题

template<class T>
struct callable_trait
{};

template<class R, class... Args>
struct callable_trait<std::function<R(Args...)>>
{
    using return_type    = R;
    using argument_types = std::tuple<Args...>;
    static constexpr size_t argument_count = sizeof...(Args);
};

template<typename callable>
using return_type = typename callable_trait<decltype(std::function{std::declval<callable>()})>::return_type;

template<typename callable>
static constexpr size_t argument_count = callable_trait<decltype(std::function{std::declval<callable>()})>::argument_count;

void f();
void g(return_type<decltype(f)>);

auto lambda = [](){};
void h(return_type<decltype(lambda)>);

void e(int, int, int);
static_assert(argument_count<decltype(e)> == 3, "oh no");
© www.soinside.com 2019 - 2024. All rights reserved.