打个码"forwarding referencable"

Make a code "forwarding referencable"

我打开了post转发参考,这是一个(希望)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

所以我的问题是:是否可以编写此代码 "forwarding referencable"?

为了完美转发你的论点,你需要推导类型。您可以通过分别推导函数的参数和函子的参数来做到这一点:

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]):

If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.

回到我们最初的片段!

现在,让我们的例子复杂一点:

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,因此类型推导A<T> 失败,因为编译器期望 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 被推断为右值引用时,T 只是 T,所以编译器期望 A<std::string>

所以问题来了:你要给一个方法一个 rvalue ,这个方法将把它转发给一个需要 lvalue[= 的函数97=]。这没错,但它可能不是您所期望的。

如何处理?

第一种可能性是强制第一个参数的类型,而不考虑 T 的推导类型,例如:

template <typename T>
void f (A<typename std::remove_reference<T>::type>, T&&);

但注意:

  1. 您将不得不添加更多内容来处理 const
  2. 当第一个参数的类型固定时(至少在您的情况下是这样),人们可能想知道 T&& 的用处。

第二种可能(warning:不知道这个是不是标准!)是把第一个参数移到最后,然后从[=37推导类型=]:

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

现在 T 的推导类型与 A 的预期类型完全匹配。

不幸的是,我不知道如何使用可变模板使上述工作...