Вопросы о лямбда-перегрузках, преобразованиях типов и совершенной пересылке - PullRequest
5 голосов
/ 10 марта 2019

Это вопрос о наборах лямбда-перегрузок и идеальной пересылке и в некотором смысле продолжение комментария . Подробнее о том, как это используется, см. другой связанный вопрос .

У меня есть несколько вопросов по приведенному ниже фрагменту кода.

Q1: Для лямбда-перегрузок я использовал overload(Fs...) -> overload<Fs...> из этого поста , но затем в этот ответ я увидел overload(Fs&&...) -> overload<std::decay_t<Fs>...>. В каких ситуациях эта разница актуальна?

Q2: Почему вы хотите определить функцию identity ниже с помощью return decltype(x)(x), а не просто return x?

В3: Можем ли мы считать 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);
    }
}

запустить его

1 Ответ

4 голосов
/ 10 марта 2019

В каких ситуациях эта разница актуальна?

Когда хотя бы один аргумент фактически является lvalue (например, identity, фактически).В этом случае соответствующий Fi является T&, то есть ссылкой на lvalue.И нельзя указывать ссылку lvalue в качестве основы для любого класса, поэтому std::decay требуется для удаления всех ссылок и cv-квалификаторов.Когда руководство по выводу принимает аргументы по значению, это автоматически не проблема.Это связано с тем, что вычет аргументов шаблона для типов значений уже «разлагает» типы.

Если вам интересно, какой использовать, то я бы сказал, что тот, у которого меньше помех, объективно лучше.Использование std::decay_t предназначено для получения того же поведения, которое было бы с версией по значению, так что можно также использовать это.

Почему вы хотите определить функцию тождества ниже свернуть decltype (x) (x), а не просто вернуть x

Это форма переадресации.Так как тип возвращаемого значения лямбды объявлен как decltype(x), нам нужно, чтобы приведение было правильно привязано к ссылке rvalue.Потому что в случае decltype(x) = T&& он не будет привязан только к x, что является lvalue.

Можем ли мы считать foo(convert(std::forward<Args>(args))...) совершенной пересылкой (для всех не преобразованных аргументов) простокак foo(std::forward<Args>(args)...)

Да.Аргументы к bar уже привязаны к ссылкам.convert позволяет этим ссылкам проходить с сохранением категории значения, так что это действительно переадресация.

...