如下的模版函数
1 2 3
| template <typename T> inline auto myFunc(T &&x) { return std::exp(std::forward<T>(x)); }
|
这段代码没问题
1 2
| double x = 0; auto y = myFunc(x);
|
推导为double myFunc<double &>(double &x)
下面这段也OK
显示实例化模版参数呢?
1 2 3 4 5 6
| { auto y = myFunc<double>(0.1); }
{ double x = 0; auto y = myFunc<double>(x); }
|
myFunc
被实例化为了double myFunc<double>(double &&x)
接收右值引用,而x
是一个左值,右值引用不能绑定一个左值,因此报错。
以下三种方式都可行
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { double x = 0; auto y = myFunc<double &>(x); }
{ double x = 0; auto y = myFunc<double>(std::move(x)); }
{ double x = 0; auto y = myFunc<double>(std::forward<double>(x)); }
|
注意完美转发std::forward<double>(x)
,其实就相当于
1
| double foo(double x) {return x;}
|
Reason
显示实例化模版函数后T&& x
中的T&&
就不在是万能引用了,模版参数T
被实例化为了double
那么函数接收的参数为double&& x
。
复杂一点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <chrono> #include <cmath> #include <tuple> #include <utility>
template <typename Func, typename... Args> inline auto measureTime(Func&& func, Args&&... args) { using FuncReturnType = decltype(func(std::forward<Args>(args)...)); using TimeDuration = decltype(std::chrono::high_resolution_clock::now() - std::chrono::high_resolution_clock::now());
const auto start = std::chrono::high_resolution_clock::now(); if constexpr (std::is_same_v<void, FuncReturnType>) { func(std::forward<Args>(args)...); auto end = std::chrono::high_resolution_clock::now(); return (end - start); } else { FuncReturnType ret = func(std::forward<Args>(args)...); auto end = std::chrono::high_resolution_clock::now(); return std::tuple<TimeDuration, FuncReturnType>{(end - start), ret}; } }
template <typename T> inline auto myFunc(T&& x) { return std::exp(std::forward<T>(x)); }
|
measureTime
,其参数func
是一个函数对象,其参数类型为Args&&...
,返回值类型为FuncReturnType
。measureTime
函数的返回值类型为TimeDuration
或者std::tuple<TimeDuration, FuncReturnType>
。用于测量func
的运行时间。
下面这段错误
1 2 3
| auto f = [](double x) { return measureTime(myFunc<double>, x); };
|
原因就是显示实例化了模版函数myFunc
,只能接受右值引用,而x
是一个左值,因此报错。
好那我们尝试不显示实例化模版函数。如下:
1 2 3 4
| { auto f = [](double x) { return measureTime(myFunc, x); }; f(0.1); }
|
报错是Candidate template ignored: couldn't infer template argument 'Func'
,唔,似乎只能显示实例化模版函数了。
1 2 3 4 5 6
| { auto f = [](double x) { return measureTime(myFunc<double>, std::forward<double>(x)); }; f(0.1); double x = 0.1; f(x); }
|
等一下,下面这段也是可行的
1 2 3 4 5 6 7 8 9
| { auto f = [](auto &&x) { using ArgType = decltype(x); return measureTime(myFunc<ArgType>, std::forward<ArgType>(x)); }; f(0.1); double x = 0.1; f(x); }
|
Achieved
measureTime
,其参数func
是一个函数对象,其参数类型为Args&&...
,返回值类型为FuncReturnType
。measureTime
函数的返回值类型为TimeDuration
或者std::tuple<TimeDuration, FuncReturnType>
。用于测量func
的运行时间。
computeDerivative
,其参数x
是一个引用类型,其参数类型为T&&
,返回值类型为auto
。用于计算f
在x
处的导数。
myFunc
,其参数x
是一个引用类型,其参数类型为T&&
,返回值类型为auto
。用于计算exp(x)
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <chrono> #include <cmath> #include <tuple> #include <utility>
template <typename Func, typename... Args> inline auto measureTime(Func&& func, Args&&... args) { using FuncReturnType = decltype(func(std::forward<Args>(args)...)); using TimeDuration = decltype(std::chrono::high_resolution_clock::now() - std::chrono::high_resolution_clock::now());
const auto start = std::chrono::high_resolution_clock::now(); if constexpr (std::is_same_v<void, FuncReturnType>) { func(std::forward<Args>(args)...); auto end = std::chrono::high_resolution_clock::now(); return (end - start); } else { FuncReturnType ret = func(std::forward<Args>(args)...); auto end = std::chrono::high_resolution_clock::now(); return std::tuple<TimeDuration, FuncReturnType>{(end - start), ret}; } }
template <typename T, typename Func> inline auto computeDerivative(T&& x, Func&& f) { return (f(x + 1e-6) - f(x)) / 1e-6; }
template <typename T> inline auto myFunc(T&& x) { return std::exp(std::forward<T>(x)); }
|
以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "test.h"
int main() { auto f = [](auto&& x) { return measureTime(myFunc, x); };
auto der_f = [](auto&& x) { return measureTime(computeDerivative, x, myFunc); };
using DataType = double; auto f_d = [](DataType x) { return measureTime(myFunc<DataType>, x); }; }
|
会在12行,也就是声明lambda对象f_d
时报错。错误信息为:
rvalue reference to type ‘double’ cannot bind to lvalue of type ‘double’
因为myFunc
的参数类型为T&&
,而f_d
的参数类型为DataType
,DataType
是double
的别名,因此f_d
的参数类型为double
,而double
是一个左值类型,因此f_d
的参数类型为左值类型,而myFunc
的参数类型为右值引用类型,因此f_d
的参数类型与myFunc
的参数类型不匹配,因此报错。
解决方法:就这个问题而言MyFunc
可以将参数类型T&&
修改为const T&
,然后删去完美转发函数。
1 2 3 4
| template <typename T> inline auto myFunc(const T& x) { return std::exp((x)); }
|
可是,如果,意外的是。没有修改MyFunc
。我们修改了lambda的声明
1 2 3
| auto f_d = [](DataType x) { return measureTime(myFunc<DataType>, std::forward<DataType>(x)); };
auto f_d = [](DataType x) { return measureTime(myFunc<double&>, x); };
|
也是行的。唔,然后继续到导数,如果没有修改声明,那么下面的代码
1 2 3 4
| auto der_f_d = [](DataType x){ return measureTime(computeDerivative<DataType, DataType (*)(DataType)>, std::forward<DataType>(x), myFunc<DataType>); };
|
修改,要么用lambda在包一层。或者修改函数的声明。
解决办法:修改函数的声明。
1 2 3 4 5 6 7 8 9
| template <typename T, typename Func> inline auto computeDerivative(const T& x, Func&& f) { return (f(x + 1e-6) - f(x)) / 1e-6; }
template <typename T> inline auto myFunc(const T& x) { return std::exp((x)); }
|
lambda的声明改为
1 2 3 4
| auto der_f_d = [](DataType x){ return measureTime(computeDerivative<DataType, DataType (*)(const DataType&)>, x, myFunc<DataType>); };
|
或者
修改函数声明
1 2 3 4 5 6 7 8 9 10
| template <typename T, typename Func> inline auto computeDerivative(T&& x, Func&& f) { auto&& y = x + 1e-6; return (f(std::forward<T>(y)) - f(std::forward<T>(x))) / 1e-6; }
template <typename T> inline auto myFunc(T&& x) { return std::exp(std::forward<T>(x)); }
|
然后lambda声明为
1 2 3 4
| auto der_f_d = [](DataType x) { return measureTime(computeDerivative<DataType, DataType (*)(DataType&&)>, std::forward<DataType>(x), myFunc<DataType>); };
|
一个不用修改函数声明的方法是,再包一层lambda
函数声明为
1 2 3 4 5 6 7 8 9
| template <typename T, typename Func> inline auto computeDerivative(T&& x, Func&& f) { return (f(x+1e-6) - f(x)) / 1e-6; }
template <typename T> inline auto myFunc(T&& x) { return std::exp(std::forward<T>(x)); }
|
然后这么定义:
1 2 3
| auto ff = [](DataType x) { return myFunc(x); };
auto der_f_d = [&ff](DataType x) { return computeDerivative(x, ff); };
|
结论
多用const T&
。