Я открыл это сообщение о пересылке ссылки, это (надеюсь) код 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 не сделал вывод.

Итак, мой вопрос: можно ли сделать этот код «переадресуемым»?

1
justHelloWorld 25 Апр 2016 в 17:37

2 ответа

Лучший ответ

Чтобы довести аргументы до совершенства, вам нужно вывести типы. Вы можете сделать это, выведя аргументы функции и параметры функтора по отдельности:

template <typename ReturnType, typename... FunArgs, typename... Args>
ReturnType callFunction(std::function<ReturnType(FunArgs...)> memFunc,
                        Args&&... args) 
{
    //...
}

Затем вы можете вызвать callFunction без параметров шаблона и все вывести:

mem.callFunction(sortFunc, vec);
2
TartanLlama 25 Апр 2016 в 15:01

Я добавлю немного деталей относительно ответа @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&&);

Но обратите внимание:

  1. Вам нужно будет добавить больше вещей, чтобы иметь дело с const.
  2. Можно задаться вопросом о полезности T&&, когда тип первого аргумента фиксирован (по крайней мере, в вашем случае).

Вторая возможность ( предупреждение: я не знаю, стандартно ли это! ) - переместить первый параметр в конец, а затем определить тип из t:

template <typename T>
void f (T &&t, A<decltype(std::forward<T>(t))>);

Теперь у вас есть точное соответствие между выведенным типом для T и ожидаемым типом для A.

К сожалению, я не знаю, как заставить это работать с вариативными шаблонами ...

0
Holt 26 Апр 2016 в 07:20