这是关于lambda重载集和完美转发以及对comment的后续跟进的问题。有关如何使用此方法的更多信息,请参见another related question。
我对以下代码段有疑问。
问题1:对于lambda重载,我使用的是this post中的overload(Fs...) -> overload<Fs...>
,但是随后在this answer中看到了overload(Fs&&...) -> overload<std::decay_t<Fs>...>
。这种差异在什么情况下有意义?
问题2:为什么要在下面用identity
而不是return decltype(x)(x)
来定义return x
函数?
Q3:是否可以像foo(convert(std::forward<Args>(args))...)
一样将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);
}
}
答案 0 :(得分:4)
这种差异在什么情况下有意义?
当至少一个参数实际上是一个左值时(实际上像identity
)。在这种情况下,相应的Fi
是T&
,即左值引用。而且不能将左值引用列为任何类的基础,因此需要std::decay
才能删除所有引用和cv限定词。当推导指南按值接受参数时,它自动是非问题。这是因为值类型的模板参数推导已经“衰减”了这些类型。
如果您想使用哪个,那么客观地说,杂乱程度较小的那个更好。使用std::decay_t
是为了获得与按值版本相同的行为,因此也可以使用它。
为什么要在下面用return decltype(x)(x)而不是仅返回x来定义恒等函数
这是一种转发形式。由于lambda的返回类型声明为decltype(x)
,因此我们需要进行强制转换以确保其正确绑定到右值引用。因为在decltype(x) = T&&
的情况下,它不会单独绑定到x
,这是一个左值。
我们能像
foo(convert(std::forward<Args>(args))...)
一样认为foo(std::forward<Args>(args)...)
是完美的转发(对于所有未转换的参数)
是的。 bar
的参数已绑定到引用。 convert
允许那些引用在保留值类别的情况下通过,因此它确实在转发。