Я открыл это сообщение о пересылке ссылки, это (надеюсь) код MCVE:
#include <functional>
#include <vector>
using namespace std;
struct MultiMemoizator {
template <typename ReturnType, typename... Args>
ReturnType callFunction(std::function<ReturnType(Args...)> memFunc, Args&&... args) {
}
};
typedef vector<double> vecD;
vecD sort_vec (const vecD& vec) {
return vec;
}
int main()
{
vecD vec;
std::function<vecD(const vecD&)> sortFunc(sort_vec);
MultiMemoizator mem;
mem.callFunction<vecD, vecD>(sortFunc, vec);
}
Поскольку это не весь код, возможно, мне придется добавить дополнительный код на основе ответов.
В любом случае, как было предложено в этом ответе, ссылка на переадресацию в этой версии невозможна, поскольку Args
не сделал вывод.
Итак, мой вопрос: можно ли сделать этот код «переадресуемым»?
2 ответа
Чтобы довести аргументы до совершенства, вам нужно вывести типы. Вы можете сделать это, выведя аргументы функции и параметры функтора по отдельности:
template <typename ReturnType, typename... FunArgs, typename... Args>
ReturnType callFunction(std::function<ReturnType(FunArgs...)> memFunc,
Args&&... args)
{
//...
}
Затем вы можете вызвать callFunction
без параметров шаблона и все вывести:
mem.callFunction(sortFunc, vec);
Я добавлю немного деталей относительно ответа @TartanLlama на почему ваш код не может компилироваться (даже без явных параметров шаблона), но также и почему (по моему мнению) ваш код опасен .
В дальнейшем я буду использовать только простой тип T
вместо вашего пакета параметров Args...
, потому что его проще объяснить и он не меняет смысла.
Небольшое напоминание о пересылке ссылок ...
Во-первых, давайте рассмотрим более простой пример, чем ваш:
template <typename T>
void f (T&&);
Теперь давайте создадим экземпляр f
из различных источников, предположим, что у него есть следующие переменные:
std::string s;
const std::string cs;
...тогда:
f(s); // instanciate f<std::string&>
f(cs); // instanciate f<const std::string&>
f(std::string()); // instanciate f<std::string&&>
Вам должно быть интересно: Почему первый экземпляр f<std::string&>
вместо f<std::string>
? , но стандарт говорит вам (§14.8.2.1 # 3 [temp.deduct .call] ):
Если P - ссылка пересылки, а аргумент - lvalue, тип «lvalue ссылка на A» используется вместо A для определения типа.
Вернемся к нашему первоначальному фрагменту!
Теперь немного усложним наш пример:
template <typename T>
struct A {};
template <typename T>
void f (A<T>, T&&);
И один экземпляр:
std::string s;
A<std::string> as;
f(as, s);
Вышеупомянутое эквивалентно вашему примеру и не будет скомпилировано, но почему ...? Итак, как объяснялось выше, когда у вас есть lvalue , выводимый тип для T&&
- это T&
, а не T
, и, таким образом, вывод типа не выполняется для { {X3}}, потому что компилятор ожидает A<std::string&>
, а вы даете A<std::string>
.
Итак, теперь мы знаем, что нам нужно сделать следующее:
A<std::string&> ars;
A<std::string const&> acrs;
f(ars, s); // good
f(acrs, cs); // good
Почему это опасно?
Хорошо, теперь это должно быть хорошо:
A<std::string&&> arrs;
f(arrs, std::string());
Но это не так ... Потому что, когда T
выводится как ссылка rvalue, T
просто T
, поэтому компилятор ожидает A<std::string>
.
Итак, вот проблема: вы собираетесь дать rvalue методу, который будет перенаправлять его функции, ожидающей lvalue . В этом нет ничего плохого, но, вероятно, это не то, чего вы ожидали.
Как с этим бороться?
Первая возможность - указать тип первого параметра независимо от выведенного типа для T
, например:
template <typename T>
void f (A<typename std::remove_reference<T>::type>, T&&);
Но обратите внимание:
- Вам нужно будет добавить больше вещей, чтобы иметь дело с
const
. - Можно задаться вопросом о полезности
T&&
, когда тип первого аргумента фиксирован (по крайней мере, в вашем случае).
Вторая возможность ( предупреждение: я не знаю, стандартно ли это! ) - переместить первый параметр в конец, а затем определить тип из t
:
template <typename T>
void f (T &&t, A<decltype(std::forward<T>(t))>);
Теперь у вас есть точное соответствие между выведенным типом для T
и ожидаемым типом для A
.
К сожалению, я не знаю, как заставить это работать с вариативными шаблонами ...
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C++ — это язык программирования общего назначения. Изначально он разрабатывался как расширение C и имел аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде, который будет скомпилирован с помощью компилятора C++. Используйте тег версии для вопросов, связанных с конкретной стандартной версией [C++11], [C++14], [C++17], [C++20] или [C++23]. и т.д.