首页 > 解决方案 > 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))

表明第二个在发布(优化)构建中有点慢。

标签: c++c++11templateseigencopy-elision

解决方案


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


推荐阅读