C++ 如何在C++;
我尝试做的一些背景知识: 我正在尝试实现一个做量子力学的库。由于量子力学基本上只是线性代数,我正在使用下面的犰狳线性代数库。Armadillo使用惰性求值对矩阵进行了一些巧妙的处理,这从实际情况中提供了一个非常好的抽象,看起来很接近matlab代码 我想做一些类似的事情,但我也希望能够使用C++ 如何在C++;,c++,c++11,templates,C++,C++11,Templates,我尝试做的一些背景知识: 我正在尝试实现一个做量子力学的库。由于量子力学基本上只是线性代数,我正在使用下面的犰狳线性代数库。Armadillo使用惰性求值对矩阵进行了一些巧妙的处理,这从实际情况中提供了一个非常好的抽象,看起来很接近matlab代码 我想做一些类似的事情,但我也希望能够使用auto,这在犰狳(或eigen)身上是不可能的 我已经环顾了一下,这个答案包含了我认为是实现这一点的典型方式: 这种方法的问题在于,当您编写 auto C = A+B; 您将得到一个C,它是一个matrix
auto
,这在犰狳(或eigen)身上是不可能的
我已经环顾了一下,这个答案包含了我认为是实现这一点的典型方式:
这种方法的问题在于,当您编写
auto C = A+B;
您将得到一个C
,它是一个matrix\u add
,而不是matrix
。即使matrix\u add
的行为类似于matrix
,但matrix\u 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
但是
这是违反直觉的。由于数学直觉行为是我想要实现的,这是一个问题
更糟糕的是当我这样做的时候
auto sumMatrices(const matrix& A, const matrix& B)
{
return A+B;
}
它返回一个矩阵\u 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_;
}
我省略了所有使matrix\u add
表现得像矩阵
的部分。这个想法是让对象引用外部对象A和B,只要它是用A+B构造的,但是当它被移动构造时,它将拥有副本
我的问题基本上是:这有效吗
我一直在想,在某些或所有情况下,移动构造函数可能会被忽略,这可能是毁灭性的
此外,是否有其他方法可以实现同样的目标?我一直在寻找,但似乎对于线性代数,至少它要么是懒惰的,要么是自动的
编辑:多亏有人提醒我“表达式模板”这个词,我的谷歌搜索结果好多了。我发现了这篇reddit帖子:以及参考文献,它们允许自动指定“类型转换”。这将是真正使所有这些都能工作的功能 您可以编写一个模板函数
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, "");
}
#包括
#包括
结构矩阵{};
结构矩阵{
矩阵算子()()常数;
};
矩阵加运算符+(矩阵常数a、矩阵常数b);
模板decltype(自动)求值(T&&val){return std::forward(val);}
矩阵求值(矩阵_add const&lazy){return lazy();}
矩阵求值(矩阵_add&lazy){return lazy();}
矩阵求值(矩阵_add&&lazy){return lazy();}
int main()
{
自动a=矩阵();
自动b=矩阵();
自动c=评估(a+b);
自动d=评估(1+2);
静态断言(std::is_same::value,“”);
静态断言(std::is_same::value,“”);
}
使用c++17类模板参数推断,您可以编写
struct matrix_expr_foo {};
struct matrix_expr_bar {};
template< typename L, typename R >
struct matrix_add {
// ...
};
matrix_add<matrix_expr_foo,matrix_expr_bar> operator + (matrix_expr_foo const& a, matrix_expr_bar const& b);
template< typename T >
struct expr {
expr( T const& expr ){
// evaluate expr ( to be stored in an appropriate member )
}
// ...
};
int main()
{
auto a = matrix_expr_foo();
auto b = matrix_expr_bar();
expr c = a + b;
/* different naming ?
auto_ c = a + b;
...
*/
}
struct matrix_expr_foo{};
结构矩阵_expr_bar{};
模板
结构矩阵{
// ...
};
矩阵加法运算符+(矩阵表达式foo const&a,矩阵表达式bar const&b);
模板
结构表达式{
expr(T const&expr){
//evaluate expr(存储在适当的成员中)
}
// ...
};
int main()
{
自动a=矩阵_expr_foo();
自动b=矩阵表达式条();
expr c=a+b;
/*不同的命名?
自动_uuC=a+b;
...
*/
}
其中
expr
将充当表达式模板的auto
。我将定义一个新操作符:eager\u eval
,如下所示:
namespace lazy {
template<class T>
void eager_eval(T const volatile&)=delete;
template<class T>
struct expression {
template<class D,
std::enable_if_t<std::is_base_of<expression, std::decay_t<D>>{}, int> =0
>
friend T eager_eval( D&& d ) { return std::forward<D>(d); }
};
}
现在,任何人都可以做到:
auto e = eager_eval( a+b );
ADL找到了正确的类型来计算惰性表达式
您可以选择实现一个返回其参数的默认eager\u eval
:
template<class T, class...Ts>
T eager_eval(T&& t, Ts&&...) { return std::forward<T>(t); }
允许您不知道传递给eager\u eval
的类型;如果它是一种通过eager\u eval
重载意识到懒惰的类型,它会转换,如果不是,则不会转换
上面的lazy::eanger\u eval中的包是为了确保它作为重载具有最低的优先级。我认为,您的基本问题是,lazy求值不能与不断变化的状态很好地结合。我认为有两种可能的途径:
matrix\u add
实现在对其求值时用matrix
对象自动替换自身,确保每次求值最多只执行一次matrix\u添加
像矩阵一样工作的对象,而是创建matrix\u函数
对象,对一些输入矩阵进行操作以产生一些结果。这允许您在您认为合适的地方显式执行评估,并重用您定义的函数。然而,这种方法将导致大量额外的代码复杂性我不认为通过引入强制评估的隐含点来解决这个问题是一个好主意:你会失去大部分可以通过懒惰评估实现的东西,那么为什么首先要麻烦呢?只有我的两分钱。是的,表达式模板和
auto
不能很好地混合。希望将来会增加一个机械师来满足您的需求。您可以考虑在al中使用
struct matrix_add:
lazy::expression<matrix>
{
matrix_add(matrix const& a, matrix const& b) : a(a), b(b) { }
operator matrix() && { // rvalue ref qualified as it should be.
matrix result;
// Do the addition.
return result;
}
private:
matrix const& a, b;
};
auto e = eager_eval( a+b );
template<class T, class...Ts>
T eager_eval(T&& t, Ts&&...) { return std::forward<T>(t); }
using lazy::eager_eval;
auto x = eager_eval( 1+2 );