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包含引用A并B使其难以随身携带的事实.例如
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/
和引用的论文,它们允许将"强制转换"指定为自动.这将是真正使所有这些工作的功能.
您可以编写一个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)