我正在查看std::function
实施及其调用operator()
template<typename Ret, typename... ArgTypes>
Ret function< Ret (ArgTypes...)>::operator()(ArgTypes...args) const
{
// some stuff
return invoker(functor, std::forward<ArgTypes>(args)...);
}
我特别疑惑,为什么在这里使用std::forward
?这有什么与完美转发有关吗?
因为只有当operator()
是具有可变参数模板声明template<typename... Args>
的模板(它不是,声明是std :: function 的部分特化)时才能完成转发。 )。
在这里使用std :: forward的意图是什么?我很困惑: - )?
答案 0 :(得分:4)
这是正确的,这不是典型的“完美转发”方案。一个简短的例子可以帮助说明动机。假设类型A
具有检测构造函数和析构函数:
#include "A.h"
#include <functional>
#include <iostream>
int
main()
{
A a1{1};
A a2{2};
std::function<void(A, A&)> f{[](A x, A& y){}};
f(a1, a2);
}
这将输出:
A(int state): 1
A(int state): 2
A(A const& a): 1
A(A&& a): 1
~A(1)
~A(-1)
~A(2)
~A(1)
说明:
a1
和a2
构建在堆栈上。然后,当传递到function
调用者时,首先复制a1
以绑定到第一个按值参数,然后在{em>移动的std::forward<A>
上调用a1
它从by-value参数到lambda。
相反,无需复制a2
以绑定function
A&
参数,然后调用std::forward<A&>(a2)
,将a2
转发为A&
lvalue而不是rvalue,这绑定到lambda的~A(-1)
参数。
然后事情就会被破坏。 A
表示使用此已检测的A
在移动构建状态中销毁ArgTypes
。
总而言之,即使ArgTypes
没有像通常的完美转发习惯用法那样推断,我们仍然希望将按值ArgTypes
转发为rvalues,并按引用std::forward
转发作为左值。所以 $
declare @result AS varchar(50)
DECLARE @peopleId AS varchar(50)
if('232332' NOT LIKE '%[^0-9]%')
BEGIN
SET @result='Numeric'
PRINT @result
END
ELSE
BEGIN
set @result='nonNumeric'
print @result
END
select isnull(class.grade,'') as grade,ISNULL(class.room,'') as room,student.prefix as prefix,student.student_id as student_id,(person.first_name+' '+person.last_name) as name,
person.dob as dob,person.people_id as people_id,quit_on,
case when student.student_status='30' then
N'พักการเรียน'
when student.student_status='31' then
N'น.ร.ไปเรียนโครงการฯ'
else ''
end
as quit_reason from school_student student
inner join people_person person on student.person_id=person.id
left join school_classroom_students classStudent on classStudent.student_id=student.id
left join school_classroom class on class.id =classStudent.classroom_id
where student.student_status in('30','31') and student.system_status = 'DC' and student.school_id=@schoolId
AND case
WHEN
@result='nonNumeric' then-- this should execure
person.people_id=@peopleId
else---- this should work
person.first_name+' '+ person.last_name LIKE '%'+@peopleId+'%'
碰巧恰好做了我们想要的。
答案 1 :(得分:1)
我觉得你对这里的很多事感到困惑。
首先,完美转发与可变参数模板无关。您可以创建一个包装类,该类包含一个函数,该函数接受一个参数并将其转发给包装对象:
template<typename T>
struct Wrapper {
template<typename Arg>
decltype(auto) test(Arg&& arg) {
return t.test(std::forward<Arg>(arg));
}
T t;
};
注意在没有任何可变参数模板的情况下使用完美转发。如果t.test
只需要移动类型作为参数,则无法在没有forward<Arg>(arg)
的情况下调用它。
这里发生的第二件事是&&
没有遵循的参数。将&&
添加到ArgTypes
将是一个错误,并且会使某些情况无法编译。考虑一下这个简单的案例:
std::function<void(int)> f;
int i = 0;
f(i);
那将无法编译。如果您将&&
添加到ArgTypes
,则每个非引用的参数(例如int
)都将成为调用运算符的右值引用(在我们的示例中为int&&
) 。由于所有参数类型都已在std::function
参数列表中正确限定,因此您希望在调用运算符中接收的内容正是那些未转换的类型。
如果您不使用std::forward
,您需要&&
的原因?因为即使您不需要推断值类别,仍然需要不将每个参数复制到包含的函数中。如果其中一个std::function
参数为int&
,则您不想移动它。但如果其中一个参数是std::unique_ptr<int>
,则必须移动它!这正是std::forward
的用途。只移动应该移动的东西。
答案 2 :(得分:0)
std::forward
只是将rvalue引用附加到类型中,因此将参考折叠规则考虑在内,它会有效地传递引用参数并移动对象参数。