有关Lambda重载,类型转换和完美转发的问题 [英] Questions on lambda overloads, type conversions and perfect forwarding
问题描述
这是关于lambda重载集和完美转发的问题,也是对另一个相关问题.
This is a question on lambda overload sets and perfect forwarding and somewhat of a followup to a comment. For more context of how this is used see another related question.
我对以下代码段有疑问.
I have some questions on the below code snippet.
Q1:对于lambda重载,我使用的是overload(Fs&&...) -> overload<std::decay_t<Fs>...>
.这种差异在什么情况下有意义?
Q1: For lambda overloads, I was using overload(Fs...) -> overload<Fs...>
from this post, but then in this answer I saw overload(Fs&&...) -> overload<std::decay_t<Fs>...>
. In what situations is this difference relevant?
Q2:为什么要用return decltype(x)(x)
而不是return x
定义下面的identity
函数?
Q2: Why would you want to define the identity
function below with return decltype(x)(x)
and not just return x
?
Q3:我们可以像foo(std::forward<Args>(args)...)
一样认为foo(convert(std::forward<Args>(args))...)
是完美的转发(对于所有未转换的参数)吗?
Q3: Can we consider foo(convert(std::forward<Args>(args))...)
as perfect forwarding (for all not-converted arguments) just like foo(std::forward<Args>(args)...)
?
#include <utility>
#include <iostream>
/////////////////////////////////////////////////////////////////////////////////
struct Foo {
virtual ~Foo() = default;
};
struct FooA: public Foo {
static void foo(const FooA&, int) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
struct FooB: public Foo {
static void foo(int, const FooB&) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
/////////////////////////////////////////////////////////////////////////////////
template<class...Fs>
struct overload:Fs... {
using Fs::operator()...;
};
// Q1: In what situations is needed over `overload(Fs...) -> overload<Fs...>`?
template<class...Fs>
overload(Fs&&...) -> overload<std::decay_t<Fs>...>;
/////////////////////////////////////////////////////////////////////////////////
// Q2: What is the purpose of `return decltype(x)(x)` over `return x`?
auto identity=[](auto&&x)->decltype(x){return decltype(x)(x);};
template<typename SpecificFoo, typename... Args>
void bar(Args&&... args) {
auto convert = overload{
[](const Foo& f){return dynamic_cast<const SpecificFoo&>(f);},
identity
};
// Q3: Is our definition of `convert` "perfectly forwarding", like if we just called
// `foo(std::forward<Args>(args)...)`, or in what situations might this not do the
// same thing (for not-converted types)?
SpecificFoo::foo(convert(std::forward<Args>(args))...);
}
/////////////////////////////////////////////////////////////////////////////////
int main() {
{
FooA specific_foo;
const Foo& foo {specific_foo};
// assume we only have access to foo when calling bar
bar<FooA>(foo, 23);
}
{
FooB specific_foo;
const Foo& foo {specific_foo};
// assume we only have access to foo when calling bar
bar<FooB>(42, foo);
}
}
推荐答案
这种差异在什么情况下有用?
In what situations is this difference relevant?
当至少一个参数实际上是一个左值时(实际上类似于identity
).在这种情况下,相应的Fi
是T&
,即左值引用.而且不能将左值引用作为任何类的基础列出,因此需要std::decay
才能删除所有引用和cv限定词.当推导指南按值接受参数时,它自动是非问题.这是因为值类型的模板参数推导已经衰减"了这些类型.
When at least one argument is in fact an lvalue (like identity
, in fact). In which case the corresponding Fi
is a T&
, i.e. an lvalue reference. And one can't list an lvalue reference as a base of any class, so std::decay
is required to remove all reference and cv-qualifiers. When the deduction guide takes arguments by value, it's automatically a non-issue. This is because template argument deduction for value types already "decays" the types.
如果您想使用哪个,那么从客观上讲,杂乱程度较小的那个更好. std::decay_t
的使用是为了获得与按值版本相同的行为,因此也可以使用它.
If you wonder which one to use, then I'd say the one with less clutter is objectively better. The use of std::decay_t
is for getting the same behavior one would get with the by-value version, so one may as well use that.
为什么要在下面用return decltype(x)(x)而不是仅仅返回x来定义恒等函数
Why would you want to define the identity function below with return decltype(x)(x) and not just return x
这是一种转发形式.由于lambda的返回类型声明为decltype(x)
,因此我们需要进行强制转换以确保其正确绑定到右值引用.因为在decltype(x) = T&&
情况下,它不会单独绑定到x
,这是一个左值.
This is a form of forwarding. Since the return type of the lambda is declared to be decltype(x)
, we need the cast to make sure it binds correctly to an rvalue reference. Because in the case decltype(x) = T&&
, it will not bind to x
alone, which is an lvalue.
我们能像
foo(std::forward<Args>(args)...)
是的. bar
的参数已绑定到引用. convert
允许那些引用在保留值类别的情况下通过,因此它确实在转发.
Yes. The arguments to bar
already bound to references. convert
lets those references pass through with value category preserved, so it's forwarding indeed.
这篇关于有关Lambda重载,类型转换和完美转发的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!