如何在C++中将惰性求值与auto结合起来

til*_*llh 17 c++ templates c++11

关于我尝试做的一些背景:我正在尝试实现一个做量子力学的库.由于量子力学基本上只是线性代数,我使用下面的犰狳线性代数库.Armadillo使用惰性求值来对矩阵做一些聪明的技巧,这样可以很好地抽象出实际情况并且看起来接近matlab代码.

我想做类似的事情,但我也希望能够使用auto,这是犰狳(或本征)无法实现的.

我一直在寻找一下,这个答案包含了我认为实现这个的典型方法:https://stackoverflow.com/a/414260/6306265

这种方法的问题在于你写的时候

auto C = A+B;
Run Code Online (Sandbox Code Playgroud)

你得到的C是一个matrix_add,而不是一个matrix.即使matrix_add表现得足够相似matrix,matrix_add包含引用AB使其难以随身携带的事实.例如

auto A = matrix(2,2,{0,1,0,1});
auto B = matrix(2,2,{1,0,1,0});
auto C = A+B;
C.printmatrix(); // 1,1 ; 1,1
Run Code Online (Sandbox Code Playgroud)

auto A = matrix(2,2,{0,1,0,1});
auto B = matrix(2,2,{1,0,1,0});
auto C = A+B;
A(0,0) = 1;
C.printmatrix(); // 2,1 ; 1,1
Run Code Online (Sandbox Code Playgroud)

这是违反直觉的.由于数学上直观的行为是我想要实现的,这是一个问题.

更糟糕的是当我这样做

auto sumMatrices(const matrix& A, const matrix& B)
{
    return A+B;
}
Run Code Online (Sandbox Code Playgroud)

返回a matrix_add引用本地内存.

我真的希望能够拥有漂亮的重载行为,但也能够使用auto.我的想法是创建一个可以包含引用或实例的包装器:

template<class T>
class maybe_reference
{
public:
    maybe_reference(const T& t):
        ptr_(std::make_unique<T>(t)),
        t_(*ptr_)
    {}
    maybe_reference(std::reference_wrapper<const T> t): 
        t_(t.get())
    {}

    const T& get(){return t_;}
private:
    unique_ptr<T> ptr_;
    const T& t_;
}
Run Code Online (Sandbox Code Playgroud)

它可能无法以这种方式实现,但一般的想法是有两个可以清楚区分的构造函数,以确保get()返回引用的对象或者引用的对象unique_ptr.

修改matrix_add:

class matrix_add {
public:
    friend matrix_add operator+(const matrix& A, const matrix& B);

    matrix_add(matrix_add&& other): A_(other.A_.get()), B_(other.B_.get()){}
private:
    matrix_add(const matrix& A, const matrix& B): A_(std::ref(A)), B_(std::ref(B)){}

    maybe_reference<matrix> A_;
    maybe_reference<matrix> B_;
};
Run Code Online (Sandbox Code Playgroud)

我遗漏了所有matrix_add表现得像的东西matrix.想法是让对象引用外部对象A和B,只要它是用A + B构造的,但是当它被移动构造时,它将拥有副本.

我的问题基本上是:这有用吗?

我一直认为移动构造函数可能会在某些或所有情况下被删除,这可能是毁灭性的.

另外,还有其他方法可以达到同样的效果吗?我一直在寻找,但似乎对于线性代数至少它是懒惰的还是自动的.

编辑:感谢被提醒"表达模板"一词,我的谷歌搜索更有成效.我找到了这个reddit-post:https://www.reddit.com/r/cpp/comments/4puabu/news_about_operator_auto/
和引用的论文,它们允许将"强制转换"指定为自动.这将是真正使所有这些工作的功能.

Ric*_*ges 1

您可以编写一个evaluate默认为 NOP 的模板函数,然后根据需要进行重载。

#include <utility>
#include <type_traits>

struct matrix {};
struct matrix_add {
  matrix operator()() const;
};

matrix_add operator + (matrix const& a, matrix const& b);


template<class T> decltype(auto) evaluate(T&& val) { return std::forward<T>(val); }
matrix evaluate(matrix_add const& lazy) { return lazy(); }
matrix evaluate(matrix_add & lazy) { return lazy(); }
matrix evaluate(matrix_add && lazy) { return lazy(); }



int main()
{
  auto a = matrix();
  auto b = matrix();

  auto c = evaluate(a + b);
  auto d = evaluate(1 + 2);

  static_assert(std::is_same<decltype(c), matrix>::value, "");
  static_assert(std::is_same<decltype(d), int>::value, "");

}
Run Code Online (Sandbox Code Playgroud)