强制扣除模板参数要参考
Force deduction of template parameter to reference
以下代码不编译,因为编译器推论一个模板参数为 int
,而必须为 int &
。在这里在Coliru上看到它。
#include <iostream>
#include <utility>
template <class F, class... ArgsType>
void do_something(F f, ArgsType... args)
{
f(std::forward<ArgsType>(args)...);
}
int main()
{
int s = 2;
int i = 1;
auto add = [](const int a, int& sum) { sum += a; };
do_something(add, i, s);
std::cout << s << std::endl;
return 0;
}
错误:
main.cpp: In instantiation of 'void do_something(F, ArgsType ...) [with F = main()::<lambda(int, int&)>; ArgsType = {int, int}]':
main.cpp:15:27: required from here
main.cpp:7:6: error: no match for call to '(main()::<lambda(int, int&)>) (int, int)'
f(std::forward<ArgsType>(args)...);
~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:7:6: note: candidate: 'void (*)(int, int&)' <conversion>
main.cpp:7:6: note: conversion of argument 3 would be ill-formed:
main.cpp:7:6: error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
main.cpp:14:40: note: candidate: 'main()::<lambda(int, int&)>' <near match>
auto add = [](const int a, int& sum) { sum += a; };
^
main.cpp:14:40: note: conversion of argument 2 would be ill-formed:
main.cpp:7:6: error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
f(std::forward<ArgsType>(args)...);
~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
理想情况下,do_something
的第三个论点应推导为int&
。一种方法是将模板参数明确传递为
#include <iostream>
#include <utility>
template <class F, class... ArgsType>
void do_something(F f, ArgsType... args)
{
f(std::forward<ArgsType>(args)...);
}
int main()
{
int s = 2;
int i = 1;
auto add = [](const int a, int& sum) { sum += a; };
do_something<decltype(add), const int, int&>(add, i, s);
std::cout << s << std::endl;
return 0;
}
在这里在Coliru上看到它。
当解决方案起作用时,我觉得不便,因为它迫使我提供 ack_5的所有模板类型,这不是最佳的,特别是如果我有一个更复杂的示例,其中有几个参数,或者,如果我想直接插入lambda函数add
作为参数 do_something
:
do_something([](const int a, int& sum) { sum += a; }, i, s);
是否有一种更方便的方法来强制将第三个参数推定为int &
?
尝试
template <class F, class... ArgsType>
void do_something(F f, ArgsType&&... args)
// ^^^^ ---------------> perfect forwarding
{
f(std::forward<ArgsType>(args)...);
}
了解问题
您的lambda表达式的第二个参数(sum
(
auto add = [](const int a, int& sum) { sum += a; };
是 lvalue参考。
参数包,
中的ArgsType
template <class F, class... ArgsType>
void do_something(F f, ArgsType... args);
分别将变量i
和s
作为参数推荐到参数 int
, int
。
请记住, std::forward<ArgsType>(args)...
与 static_cast<ArgsType&&>(args)...
相同(即,它只是施放到 rvalue参考类型(,对do_something()
的调用将等效于调用以下功能模板:
template <class F>
void do_something(F f, int a, int b)
{
f(static_cast<int&&>(a), static_cast<int&&>(b));
}
表达式 static_cast<int&&>(b)
是 rvalue (更精确(。由于您无法用rvalue(参数(初始化lvalue参考(参数(,因此会导致汇编误差。
解决方案
您可以使用转发引用:
template <class F, class... ArgsType>
void do_something(F f, ArgsType&&... args);
参数包args
的单独参数的类型始终是参考:
- lvalue 参考类型如果通过lvalue作为参数。
- rvalue 参考类型如果将rvalue作为参数传递。
以这种方式,对于您的函数调用,参数pack ArgsType
将推荐为参数pack int&
, int&
,它等同于调用以下函数模板:
template <class F>
void do_something(F f, int& a, int& b)
{
f(static_cast<int& &&>(a), static_cast<int& &&>(b));
}
由于参考折叠适用于static_cast<int& &&>(b)
,因此表达式在static_cast<int&>(b)
中导致 CC_24,这是一个LVALUE。可以使用LVALUE初始化LVALUE参考类型。
但是,请注意:
do_something(add, i, 7);
^
|
--- rvalue
现在不会编译,因为将RVALUE作为第二个参数传递给add
。推理类似于您的原始错误。
- Lambda闭包左值可以作为右值参考参数传递
- 遍历地图并将该对用作参考参数 C++11
- 什么是常量参考参数以及如何使用它?
- 函数参数绑定通过参考与传递指针传递数组的规则
- 参考类中的模板参数包
- 是否可以通过引用通过参数返回参考
- RVALUE参考与const lVALUE参考作为参数之间的混淆
- 可以将模板字段参考模板参数作为第一个模板参数传递
- 二进制搜索树插入带参数参考
- 将参考类型作为嵌套模板结构中的模板参数作为模板参数不起作用
- 如何将rvalue作为参考参数传递给函数
- 强制扣除模板参数要参考
- 移动 l 值参考参数是否是一种不好的做法?
- C 11:为什么RVALUE参考参数隐式转换为LVALUE
- 转发模板参数的const-度,我应该使用转发参考
- 参考变量参数与本地参考变量
- 无法将类对象转换为函数默认参数中的参考
- 为什么可以将其他类型变量用作C 中常量参考参数参数的参数
- 如何处理可能指向内部数据的参考参数
- 用于未使用参数参考的单线解决方案