假设我有一个模板函数, assign()
. 它接受一个指针和一个值,并将值赋给指针的目标:
template <typename T> void assign(T *a, T b) { *a = b; }
int main() {
double i;
assign(&i, 2);
}
在这种情况下,我总是希望从第一个参数中推导出 T
,但看起来我没有很好地表达这一点 . 2
的类型是 int
,所以:
deduce.cpp:5:5: error: no matching function for call to 'assign'
assign(&i, 2);
^~~~~~
deduce.cpp:1:28: note: candidate template ignored: deduced conflicting types for parameter 'T' ('double' vs. 'int')
template void assign(T *a, T b) { *a = b; }
有没有办法可以声明 assign()
,以便第二个参数不参与模板参数推导?
7 回答
显然
std::identity
已不存在了(Is there a reason why there is not std::identity in the standard library?)但是在调用函数时,可以在参数类型列表中指定参数类型:
通过这种方式,编译器将整数输入参数转换为double以匹配函数模板,而不进行参数推导 .
Live demo
使用两个类型参数可能是最好的选择,但是如果你真的只想从第一个参数执行演绎,只需使第二个不可推导:
此答案的早期版本建议使用C 11中引入的模板别名功能 . 但模板别名仍然是可推断的上下文 .
std::identity
和std::remove_reference
阻止演绎的主要原因是模板类可以是专用的,因此即使你有一个模板类型参数的typedef,它也会发生 . {2971196_ t . 但模板别名排除了特殊化,因此仍然会发生演绎 .问题是编译器正在从第一个和第二个参数中推断出冲突的信息 . 从第一个参数,它推导
T
为double
(i
是double)
;从第二个参数,它推断T
为int
(2
的类型是int
) .这里有两个主要的可能性:
在这种情况下,您可能希望SFINAE约束模板,以便在
U
无法转换为T
的情况下它不会分离到重载分辨率:如果在
U
无法转换为T
时不需要从重载集中排除函数,则可能需要在assign()
内部生成静态断言以产生更好的编译错误:只是将值
2
推导为类型int
,它与&i
推导出的模板参数不匹配 . 您需要将该值用作double:为什么不使用两个独立的参数类型,一个用于源,一个用于目标?
如果无法进行分配,则模板实例化将无法编译 .
我的尝试看起来像这样:
第二个参数是你可以转换为
T
的任何东西,但是我们通过通用引用来获取它,并有条件地将其转换为*dest
. 我在签名中测试可转换性,而不是让主体无法编译,因为失败找到一个重载似乎比没有编译正文更礼貌 .Live example .
相比较简单:
以上节省1
move
. 如果你有一个昂贵的移动类,或一个只复制和复制昂贵的类,这可以节省大量的 .或者,您可以使用
decltype
将第二个参数类型转换为第一个参数 .