有关Lambda重载,类型转换和完美转发的问题 [英] Questions on lambda overloads, type conversions and perfect forwarding

查看:77
本文介绍了有关Lambda重载,类型转换和完美转发的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是关于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<Fs...>"rel =" noreferrer">这篇文章,但随后在此答案中,我看到了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).在这种情况下,相应的FiT&,即左值引用.而且不能将左值引用作为任何类的基础列出,因此需要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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆