1

我打开了这篇关于转发参考的帖子,这是一个(希望如此)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没有推导出来。

所以我的问题是:是否有可能使此代码“转发可引用”?

4

2 回答 2

2

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

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

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

mem.callFunction(sortFunc, vec);
于 2016-04-25T15:01:34.323 回答
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 进行类型推导。

回到我们最初的片段!

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

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&&是 is T&, not T,因此类型推导失败了,A<T>因为编译器期望A<std::string&>并且你给出了 a 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());

但它不是......因为 whenT被推断为右值引用,Tis simple T,所以编译器期待A<std::string>

所以问题来了:你要给一个方法一个右值,这个方法将把它转发给一个需要左值的函数。这没有错,但它可能不是你所期望的。

如何处理?

第一种可能性是强制第一个参数的类型,而不考虑推导的类型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

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

于 2016-04-25T20:00:16.280 回答