Как скомилировать такой шаблон

239
13 июня 2022, 03:50

Не пойму как правильно написать invoke чтоб компилировалось

#include <iostream>
#include <functional>
struct AA
{
    AA()
    {
        std::cout << "AA::AA()\n";
    }
    ~AA()
    {
        std::cout << "AA::~AA()\n";
    }
    AA(const AA&)
    {
        std::cout << "AA::AA(const AA&)\n";
    }
    
    AA(AA&&)
    {
        std::cout << "AA::AA(AA&&)\n";
    }
    AA& operator = (const AA&)
    {
        std::cout << "AA& AA::operator(const AA&)const\n";
        return *this;
    }
    
    AA& operator = (AA&&)
    {
        std::cout << "AA& AA::operator(AA&&)\n";
        return *this;
    }
};
template<class F, typename... Args>
void invoke(F func, Args... args)
{
    // Do something before
    func(std::forward<decltype(args)>(args)...);
    // Do something after
}
int main()
{
    AA aa;
    std::function<void(AA&)> ff = [](AA& aa) { std::cout << "&aa=" << &aa << "\n";};
    invoke(std::move(ff), std::move(aa));
}

Весь цимес в том что мне передают функтор ff который ожидает неконстантный референс на AA, в точке вызова invoke(...) мне не проблема отдать вледение на AA, но вот компилятор с этим не согласен:

2137849079/source.cpp: In instantiation of ‘void invoke(F, Args ...) [with F = std::function<void(AA&)>; Args = {AA}]’: 2137849079/source.cpp:48:40: required from here 2137849079/source.cpp:40:9: error: no match for call to ‘(std::function<void(AA&)>) (AA)’ func(std::forward<decltype(args)>(args)...); ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from /usr/include/c++/7/functional:58:0, from 2137849079/source.cpp:2: /usr/include/c++/7/bits/std_function.h:701:5: note: candidate: _Res std::function<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) const [with _Res = void; _ArgTypes = {AA&}] function<_Res(_ArgTypes...)>:: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/include/c++/7/bits/std_function.h:701:5: note: conversion of argument 1 would be ill-formed: 2137849079/source.cpp:40:9: error: cannot bind non-const lvalue reference of type ‘AA&’ to an rvalue of type ‘AA’ func(std::forward<decltype(args)>(args)...); ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

UPD

Вторая проблема. При таком вызове

AA aa;
std::function<void(const AA&)> ff = [](const AA& aaa) { std::cout << "&aaa=" << &aaa << "\n";};
invoke(std::move(ff), aa);

где-то вызывается конструктор копирования

> AA::AA()
> AA::AA(const AA&)
> &aaa=0x7fff8eb18faf
> AA::~AA()
> AA::~AA()
Answer 1

По-моему, главная ваша проблема - что ваша ff принимает lvalue, а впихнуть вы ей пытаетесь rvalue.

Вот такое вроде бы сработает так, как вы хотите - нет?

template<class F, typename... Args>
void invoke(F func, Args... args)
{
    // Do something before
    func(std::forward<Args>(args)...);
    // Do something after
}
int main()
{
    AA aa;
    std::function<void(AA&&)> ff = [](AA&& aa) { std::cout << "&aa=" << &aa << "\n";};
    invoke(std::move(ff), std::move(aa));
}

"По-моему, так..." (с) Пух*

К вашему UPD - зависит от компилятора. VC++ 2019 ничего не копирует...

Answer 2

Собственно проблема в std::forward, вы пытаетесь запихнуть T в T&. Лямбда не является шаблонной функцией, поэтому использование std::forward является лишним, так как сохранение типа не требуется. Простая передача обеспечит неявное приведение к T&. Ещё вы используете std::move, но при этом принимаете копию в invoke. К счастью шаблоны считают && за универсальную ссылку, что обеспечивает принятие любых аргументов без копирования.

template<class F, typename... Args>
void invoke(F&& func, Args&&... args)
{
    // Do something before
    func(args...);
    // Do something after
}

Также и при добавлении && к шаблонным аргументам, вы всё равно не сможете использовать std::forward из-за того, что попытаетесь запихнуть T&& вместо T&. Однако если вы откажетесь от std::move, тогда у вас получится, копирования всё ещё не произойдёт.

template<class F, typename... Args>
void invoke(F&& func, Args&&... args)
{
    // Do something before
    func(std::forward<Args>(args)...);
    // Do something after
}
int main()
{
    AA aa;
    decltype(auto) ff = [](AA& aa) { std::cout << "&aa=" << &aa << "\n";};
    invoke(ff, aa);
}

Всё таки, я больше склоняюсь ко второму варианту, но если у вас есть веская причина использовать std::move, то используйте первый.

READ ALSO
Как отфильтровать значение переменной в цикле?

Как отфильтровать значение переменной в цикле?

Как отфильтровать по русскому алфавиту значения одной из нескольких переменной, которые выводятся через цикл foreach?

273
Необходимо сформировать GET или POST запрос к API с JWT авторизацией

Необходимо сформировать GET или POST запрос к API с JWT авторизацией

Помогите разобраться с запросом к API с JWT авторизациейВ идеале пример кода

224
ProgressBar для ajax

ProgressBar для ajax

Прочитал https://developermozilla

400