c++ - C++(函数)模板返回其唯一参数而不为某些类型复制它
问题描述
我有一个函数 value(x) 对许多类型都进行了重载,例如:
double value(double x) { return x; }
double value(MyType x) { return x.value(); }
SomeContainer<double> value(SomeContainer<double> x) { return x; }
SomeContainer<double> value(SomeContainer<MyType> x) { ... }
其中 MyType 实际上是一个具有关于一组参数的梯度向量的数字。
用于通用(模板)程序。
我想定义:
Matrix<double> value(Matrix<double>)
Matrix<double> value(Matrix<MyType>)
我正在使用特征矩阵,这是我当前对第一个函数的实现:
template < typename Derived,
typename std::enable_if< std::is_floating_point< typename Derived::Scalar >::value, int >::type = 0 >
Derived value( const Eigen::MatrixBase< Derived >& matrix )
{
return matrix;
}
问题是这似乎效率低下,除非在编译器可以找出结果/参数没有被修改并逃避副本的可能情况下。我也无法返回对参数的引用,因为它是本地/临时的。
基本上我想要的是将 value(x) 编译为参数表达式本身,如果参数是双/浮点矩阵。我不知道如何使用函数模板来实现这一点,并且宏不允许专门化。
可以做些什么来避免复制?
编辑 2019 年 3 月 22 日:
如果我将返回类型替换为
const Derived &
我收到以下 GCC 警告:
warning: returning reference to temporary [-Wreturn-local-addr]
在以下代码中:
Matrix33 m;
m << 1, 2, 3,
4, 5, 6,
7, 8, 9;
std::cout << m << std::endl;
std::cout << value(m) << std::endl;
并且 value(m) 打印输出是垃圾。
此外,我现在越来越多地认为“返回对象本身”将是一个坏主意,因为它将在通用代码中使用:
auto m = value(Mx)
其中 Mx 是 X(模板参数)的矩阵,m 是双精度矩阵。
具有不同的语义(在 X 为 double 的情况下窃取对象并在其他情况下创建单独的对象)可能会导致许多编程错误。
另一种可能性是返回代理对象。
然而,最好的办法是让编译器查看何时不需要副本,因为没有任何更改。然而,情况似乎并非如此:我要比较的基准
Matrix<double,3,3> M = ...
norm(M)
和
Matrix<double,3,3> M = ...
norm(value(M))
表明第二个在发布(优化)构建中有点慢。
解决方案
matrix.derived()
如果要引用封装类型,则需要返回:
template < typename Derived,
typename std::enable_if< std::is_floating_point< typename Derived::Scalar >::value, int >::type = 0 >
const Derived& value( const Eigen::MatrixBase< Derived >& matrix )
{
return matrix.derived();
}
关于value(Matrix<MyType> const&)
,您可以通过它只返回一个视图的方式来实现它,基本上像这样(需要 C++14,或者更多的实现工作):
template < typename Derived,
typename std::enable_if< std::is_same< typename Derived::Scalar, MyType >::value, int >::type = 0 >
auto value( const Eigen::MatrixBase< Derived >& matrix )
{
// If `x.value()` returns by value, better use `unaryExpr` instead of `unaryViewExpr`:
return matrix.unaryViewExpr([](const MyType& x){ return x.value();} );
}
Godbolt-Link:httpsfoo1
://godbolt.org/z/qcYKnw 不会生成警告,并且
都foo2
只是尾调用该norm
函数。有一些小的开销,如果被内联foo3
,可能会被优化掉。norm
推荐阅读
- html - CSS背景图像不会被加载并且代码不会被更新
- ipaf - 如何执行包含多个流的测试套件?
- python - 在熊猫中将 34:37 转换为 34.37
- assembly - 我可以在汇编语言中使用注册变量作为被乘数吗?
- mysql - 我可以检查查询使用的是哪个连接 - laravel 读写连接吗?
- sql-server - 如何计算 SSRS 中的周转率 (%)?
- excel - 我需要从 1 日到当前日期的更新总和
- arrays - Flutter:如何发送一个数组来发布请求 API?
- excel - 每当另一行相同时,我如何每 5 行增加 1?
- java - 如何在保持抬头通知的情况下禁用通知声音?