我正在尝试从作为模板参数给出的回调中推断出回调函数参数。我已经找到了先前的答案: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时,为什么不起作用?
答案 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
是类型,而不是值。