制作代码“转发参考”

时间:2016-04-25 14:37:32

标签: c++ c++11 forwarding-reference

我打开了关于转发引用的here帖子,这是(希望)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);
}

由于这不是整个代码,我可能需要根据答案添加额外的代码。

无论如何,正如this回答中所建议的那样,此版本无法转发引用,因为Args不会被推断出来。

所以我的问题是:是否有可能使这段代码“转发参考”?

2 个答案:

答案 0 :(得分:2)

为了完善你的论点,你需要推断出类型。您可以通过单独推导函数的参数和函数的参数来完成此操作:

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

然后你可以在没有模板参数的情况下调用callFunction并推断出所有内容:

mem.callFunction(sortFunc, vec);

答案 1 :(得分:0)

我将在为什么你的代码无法编译(即使没有明确的模板参数)的情况下添加一些关于@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是转发引用且参数是a   左值,类型“左值引用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);

以上相当于你的例子,将无法编译,但为什么......?好吧,如上所述,当你有一个左值时,T&&的推断类型是T&,而不是T,因此类型推导失败{ {1}}因为编译器期待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

所以这就是问题:你要给一个方法提供一个 rvalue ,然后将它转发给一个期望 lvalue 的函数。这没错,但可能不是你想象的那样。

如何处理?

第一种可能性是强制第一个参数的类型,而不管A<std::string>的推断类型,例如:

T

但请注意:

  1. 您必须添加更多内容才能处理template <typename T> void f (A<typename std::remove_reference<T>::type>, T&&);
  2. 当第一个参数的类型被修复时(至少在你的情况下),人们可能会想知道const的用处。
  3. 第二种可能性(警告:我不知道这是否是标准的!)是在末尾移动第一个参数,然后从T&&中推断出类型:

    t

    现在,template <typename T> void f (T &&t, A<decltype(std::forward<T>(t))>); 的推断类型与T的预期类型完全匹配。

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