模板参数推导失败
Posted
技术标签:
【中文标题】模板参数推导失败【英文标题】:Template argument deduction fails 【发布时间】:2014-03-25 14:51:07 【问题描述】:我正在尝试使用标签和enable_if
对模板参数实施约束。代码如下:
#include <type_traits>
#include <iostream>
template<typename Type>
struct tag ;
struct Atag ;
struct Btag ;
template<typename Type, typename Tag>
struct tag_enabled
static_assert(
std::is_same
<
typename tag<Type>::type,
Tag
>::value,
"Error: Type is not tagged with Tag."
);
typedef typename std::enable_if
<
std::is_same
<
typename tag<Type>::type,
Tag
>::value,
Type
>::type type;
;
template<typename A>
typename tag_enabled<A, Atag>::type
worker(
typename tag_enabled<A, Atag>::type const & a
)
A result;
std::cout << "Atag -> Atag" << std::endl;
return result;
template<typename A, typename B>
typename tag_enabled<A, Atag>::type
worker(
typename tag_enabled<B, Btag>::type const & b
)
A result;
std::cout << "Btag -> Atag" << std::endl;
return result;
template<typename A, typename ... Args>
A caller(Args ... args)
return worker<A>(args ...);
struct test_a ;
struct test_b ;
template<>
struct tag<test_a>
typedef Atag type;
;
template<>
struct tag<test_b>
typedef Btag type;
;
int main(int argc, const char *argv[])
// caller int(int)
test_a ta1;
test_b tb1;
auto ta2 = caller<test_a>(ta1);
// Why does this fail?
auto ta3 = caller<test_a>(tb1);
return 0;
它会导致以下错误:
test-template.cpp: In instantiation of ‘A caller(Args ...) [with A = test_a; Args = test_b]’:
test-template.cpp:90:34: required from here
test-template.cpp:63:30: error: no matching function for call to ‘worker(test_b&)’
return worker<A>(args ...);
^
test-template.cpp:63:30: note: candidates are:
test-template.cpp:35:1: note: template<class A> typename tag_enabled<A, Atag>::type worker(const typename tag_enabled<A, Atag>::type&)
worker(
^
test-template.cpp:35:1: note: template argument deduction/substitution failed:
test-template.cpp:63:30: note: cannot convert ‘args#0’ (type ‘test_b’) to type ‘const type& aka const test_a&’
return worker<A>(args ...);
^
test-template.cpp:48:1: note: template<class A, class B> typename tag_enabled<A, Atag>::type worker(const typename tag_enabled<B, Btag>::type&)
worker(
^
test-template.cpp:48:1: note: template argument deduction/substitution failed:
test-template.cpp:63:30: note: couldn't deduce template parameter ‘B’
return worker<A>(args ...);
除了最后一个错误之外的所有错误都是预期和欢迎的。 tag_enabled
应确保不会根据模板参数标签实例化函数模板。这个错误例如:
test-template.cpp:35:1: note: template argument deduction/substitution failed:
test-template.cpp:63:30: note: cannot convert ‘args#0’ (type ‘test_b’) to type ‘const type& aka const test_a&’
return worker<A>(args ...);
很棒,因为我希望该函数的推导失败,因为它应该执行映射 Atag -> Atag
而不是 Btag -> Atag
。如果双参数函数模板可以工作,SFINAE 会(至少我希望如此)放弃这个候选函数。这是我关心的错误,为什么模板参数推导在这里失败:
test-template.cpp:48:1: note: template argument deduction/substitution failed:
test-template.cpp:63:30: note: couldn't deduce template parameter ‘B’
return worker<A>(args ...);
?
【问题讨论】:
【参考方案1】:编译器可以从模板函数参数中推导出一个类型模板参数 A
或 B
和一个非类型模板参数 N
,该模板函数参数的类型由以下结构组成 (Stroustrup 23.5 .2,iso 14.8.2.1):
A
const A
volatile A
A*
A&
A[constant_expression]
type[N]
class_template<A>
class_template<N>
B<A>
A<N>
A<>
A type::*
A A::*
type A::*
A (*)(args)
type (A::*)(args)
A (type::*)(args)
type (type::*)(args_AN)
A (A::*)(args_AN)
type (A::*)(args_AN)
A (type::*)(args_AN)
type (*)(args_AN)
其中args
是一个不允许推导的参数列表,args_AN
是一个参数列表,通过递归应用上述规则,可以从中确定A
或N
。如果不是所有的参数都可以这样推导出来,那么调用就是模棱两可的。
你的构造
typename tag_enabled<B, Btag>::type const &
不具备上述形式之一,因此无法推导出B
template<typename A, typename B>
typename tag_enabled<A, Atag>::type
worker(typename tag_enabled<B, Btag>::type const & b)
B
必须明确指定,就像std::forward 的情况一样。不幸的是,这很不方便。在允许扣除的同时,有几种方便的方法是
template<typename A, typename B, typename = typename std::enable_if <...> >
typename tag_enabled<A, Atag>::type
worker(B const& b)
或
template<typename A, typename B>
typename tag_enabled<A, Atag, B, Btag>::type
worker(B const& b)
无论哪种方式,你都必须稍微改变你的设计。
【讨论】:
当B
被标记为Btag
然后外部const
限定符被ADL 剥离时,tag_enabled 是否实际上不会导致B const &
,从而导致B&
是在您在答案中发布的表格中?
@tmaric 我可以,但编译器无法在typename tag_enabled<B, Btag>::type
内推断 B
,因为这不是上述形式之一。如果B
是给定的,那么是的,编译器会如您所说的那样生成B const &
。
@tmaric 换一种方式想一想:我可以写tag_enabled
,而typename tag_enabled<B, Btag>::type
会导致X
whatever B
是。那么,给定X
,B
会是什么?这是反转(类型)函数的问题:该函数必须是可逆的。编译器不知道。
谢谢!我正在阅读模板书,似乎其原因是参数推导是自上而下的方法:“匹配过程从顶层构造开始并通过组成元素递归”......跨度>
以上是关于模板参数推导失败的主要内容,如果未能解决你的问题,请参考以下文章
采用 Eigen::Tensor 的函数 - 模板参数推导失败
C++ 编译报错:couldn’t deduce template parameter ‘xxx’(模板参数推导失败)