gpt4 book ai didi

c++ - 在表达式模板中嵌套子表达式

转载 作者:行者123 更新时间:2023-11-30 04:46:40 25 4
gpt4 key购买 nike

我们正在编写一个表达式模板库来处理对具有稀疏梯度 vector (一阶自动微分)的值的操作。我试图弄清楚如何根据表达式是否为临时表达式来通过引用或值嵌套子表达式成为可能。

我们有一个 Scalar 类,它包含一个值和一个稀疏梯度 vector 。我们使用表达式模板(如 Eigen)来防止构造和分配过多的临时对象 Scalar对象。因此我们有类 Scalar继承自 ScalarBase<Scalar> (CRTP)。

ScalarBase< Left > 类型的对象之间的二元运算(例如+、*)和 ScalarBase< Right >返回 ScalarBinaryOp<Left, Right,BinaryOp>ScalarBase< ScalarBinaryOp<Left, Right,BinaryOp> > 继承的对象:

template< typename Left, typename Right >
ScalarBinaryOp< Left, Right, BinaryAdditionOp > operator+(
const ScalarBase< Left >& left, const ScalarBase< Right >& right )
{
return ScalarBinaryOp< Left, Right, BinaryAdditionOp >( static_cast< const Left& >( left ),
static_cast< const Right& >( right ), BinaryAdditionOp{} );
}

ScalarBinaryOp必须持有一个值或对 Left 类型的操作数对象的引用和 Right .持有人的类型由 RefTypeSelector< Expression >::Type 的模板特化定义。 .

目前这始终是一个常量引用。它目前适用于我们的测试用例,但持有对临时子表达式的引用似乎不正确或不安全。

显然我们也不希望 Scalar包含稀疏梯度 vector 的对象被复制。如果xyScalar , 表达式 x+y应该持有对 x 的常量引用和 y .但是,如果 f是来自 Scalar 的函数至 Scalar , x+f(y)应该持有对 x 的常量引用和 f(y) 的值.

因此我想传递关于子表达式是否是临时的信息。我可以将其添加到表达式类型参数中:

ScalarBinaryOp< typename Left, typename Right, typename BinaryOp , bool LeftIsTemporary, bool RightIsTemporary >

RefTypeSelector :

RefTypeSelector< Expression, ExpressionIsTemporary >::Type

但是我需要为每个二元运算符定义 4 个方法:

ScalarBinaryOp< Left, Right, BinaryAdditionOp, false, false > operator+(
const ScalarBase< Left >& left, const ScalarBase< Right >& right );
ScalarBinaryOp< Left, Right, BinaryAdditionOp, false, true > operator+(
const ScalarBase< Left >& left, ScalarBase< Right >&& right );
ScalarBinaryOp< Left, Right, BinaryAdditionOp, true, false > operator+(
ScalarBase< Left >&& left, const ScalarBase< Right >& right );
ScalarBinaryOp< Left, Right, BinaryAdditionOp, true, true > operator+(
ScalarBase< Left >&& left, ScalarBase< Right >&& right )

我更希望能够通过完美转发来实现这一点。但是我不知道如何在这里实现这一目标。首先,我不能使用简单的“通用引用”,因为它们几乎可以匹配任何东西。我想可能可以将通用引用和 SFINAE 结合起来只允许某些参数类型,但我不确定这是要走的路。另外我想知道我是否可以编码关于 Left 和 Right 最初是左值还是右值引用的信息,这些信息是参数化 ScalarBinaryOp 的 Left 和 Right 类型。而不是使用 2 个额外的 bool 参数以及如何检索该信息。

我必须支持主要与 c++11 兼容的 gcc 4.8.5。

2019/08/15 更新:实现

template < typename Expr >
class RefTypeSelector
{
private:
using Expr1 = typename std::decay<Expr>::type;
public:
using Type = typename std::conditional<std::is_lvalue_reference<Expr>::value, const Expr1&,Expr1>::type;
};
template< typename Left, typename Right, typename Op >
class ScalarBinaryOp : public ScalarBase< ScalarBinaryOp< Left, Right, Op > >
{

public:

template <typename L, typename R>
ScalarBinaryOp( L&& left, R&& right, const Op& op )
: left_( std::forward<L>(left) )
, right_( std::forward<R>(right) ))
, ...
{
...
}

...

private:
/** LHS expression */
typename RefTypeSelector< Left >::Type left_;

/** RHS expression */
typename RefTypeSelector< Right >::Type right_;

...
}

template< typename Left, typename Right,
typename Left1 = typename std::decay<Left>::type,
typename Right1 = typename std::decay<Right>::type,
typename std::enable_if<std::is_base_of<ScalarBase<Left1>, Left1>::value,int>::type = 0,
typename std::enable_if<std::is_base_of<ScalarBase<Right1>, Right1>::value,int>::type = 0 >
ScalarBinaryOp< Left, Right, BinaryAdditionOp > operator+(
Left&& left, Right&& right )
{

return ScalarBinaryOp< Left, Right, BinaryAdditionOp >( std::forward<Left>( left ),
std::forward<Right>( right ), BinaryAdditionOp{} );
}

最佳答案

您可以将左值/右值信息编码为LeftRight 类型。例如:

ScalarBinaryOp<Left&&, Right&&> operator+(
ScalarBase<Left>&& left, ScalarBase<Right>&& right)
{
return ...;
}

ScalarBinaryOp 是这样的:

template<class L, class R>
struct ScalarBinaryOp
{
using Left = std::remove_reference_t<L>;
using Right = std::remove_reference_t<R>;

using My_left = std::conditional_t<
std::is_rvalue_reference_v<L>, Left, const Left&>;
using My_right = std::conditional_t<
std::is_rvalue_reference_v<R>, Left, const Right&>;

...

My_left left_;
My_right right_;
};

或者,您可以显式地按值存储所有内容,Scalar 除外。为了能够按值存储 Scalar,您可以使用包装类:

x + Value_wrapper(f(y))

包装器很简单:

struct Value_wrapper : Base<Value_wrapper> {
Value_wrapper(Scalar&& scalar) : scalar_(std::move(scalar)) {}

operator Scalar() const {
return std::move(scalar_);
}

Scalar&& scalar_;
};

RefTypeSelector 专门针对 Value_wrapper:

template<> struct RefTypeSelector<Value_wrapper> {
using Type = Scalar;
};

二元运算符定义保持不变:

template<class Left, class Right>
ScalarBinaryOp<Left, Right> operator+(const Base<Left>& left, const Base<Right>& right) {
return {static_cast<const Left&>(left), static_cast<const Right&>(right)};
}

完整示例:https://godbolt.org/z/sJ3NfG

(我在上面使用了一些 C++17 特性只是为了简化符号。)

关于c++ - 在表达式模板中嵌套子表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56619644/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com