从自动模板参数回调中提取参数

时间:2019-02-22 10:36:13

标签: c++ c++17 template-meta-programming

我正在尝试从作为模板参数给出的回调中推断出回调函数参数。我已经找到了先前的答案:Can you extract types from template parameter function signature,看起来像是答案,但是以某种方式我无法使它满足我的需求。

我有以下代码:

#include <tuple>

template<typename S>
struct signature;

template<typename R, typename... Args>
struct signature<R(Args...)>
{
    using return_type = R;
    using argument_type = std::tuple<Args...>;
};

template <auto callback>
void check_callback()
{
    using callback_type = decltype(callback);
    using arg1          = std::tuple_element_t<0, signature<callback_type>::argument_type>;
}

int main()
{
}

当使用clang 6.0作为c ++ 17进行编译时,出现以下错误:

  错误:模板类型参数的模板参数必须为类型;   你忘了“类型名称”吗?

如果我将定义callback_type的行更改为类似

using callback_type = void(int);

然后它突然起作用,因此给定有效的函数签名,该代码似乎起作用。在来自模板参数的函数上使用decltype时,为什么不起作用?

2 个答案:

答案 0 :(得分:5)

  

为什么在来自模板参数的函数上使用decltype时不起作用?

因为在这种情况下,callback_type是从属名称,它取决于模板参数callback。然后,您必须使用关键字typename

  

在模板的声明或定义中,typename可用于声明从属名称是类型。

因此将代码更改为

using arg1 = std::tuple_element_t<0, typename signature<callback_type>::argument_type>;
//                                   ^^^^^^^^

using callback_type = void(int);没有这样的问题; callback_type是一个独立名称。

答案 1 :(得分:3)

完整的错误消息是(clang 9.0.0)

prog.cc:17:51: error: template argument for template type parameter must be a type; did you forget 'typename'?
    using arg1          = std::tuple_element_t<0, signature<callback_type>::argument_type>;
                                                  ^
                                                  typename

上面的行不是问题,但是您需要向编译器确​​保signature<callback_type>::argument_type是一种类型,因为这取决于模板参数(通过decltype(callback))。因此,您必须添加typename

using arg1          = std::tuple_element_t<0, typename signature<callback_type>::argument_type>;
                                           //   ^^

using callback_t = void(int);的情况下没有这种歧义,因为在那种情况下signature<callback_type>并不依赖模板参数callback,并且编译器已经知道signature<callback_type>::argument_type是类型,而不是值。