1

疎な勾配ベクトル (一次自動微分) を使用して値の操作を処理する式テンプレート ライブラリを作成しています。式が一時的であるかどうかに応じて、参照または値によってサブ式をネストできるようにする方法を見つけようとしています。

値とスパース勾配ベクトルを含むクラス Scalar があります。式テンプレート (Eigen など) を使用して、あまりにも多くの一時Scalarオブジェクトの構築と割り当てを防ぎます。したがって、 (CRTP)Scalarから継承するクラスがあります。ScalarBase<Scalar>

タイプのオブジェクト間の二項演算 (+、* など)と、から継承するオブジェクト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{} );
}

ScalarBinaryOpLeftは、型およびのオペランド オブジェクトへの値または参照を保持する必要がありますRight。ホルダーのタイプは、テンプレートの特殊化によって定義されRefTypeSelector< Expression >::Typeます。

現在、これは常に const 参照です。現時点では、テスト ケースでは機能しますが、一時的な部分式への参照を保持することは正しくないか、安全ではないようです。

Scalar明らかに、まばらな勾配ベクトルを含むオブジェクトがコピーされることも望ましくありません。xyがの場合Scalar、式は and へのx+yconst 参照を保持する必要がxありyます。ただし、が からまでfの関数である場合、への const 参照と の値を保持する必要があります。ScalarScalarx+f(y)xf(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 を組み合わせて特定のパラメーター タイプのみを許可することは可能かもしれないと思いますが、これが正しい方法かどうかはわかりません。ScalarBinaryOpまた、2 つの追加のブール値パラメーターを使用する代わりに、Left と Right が元々左辺値または右辺値参照であったかどうか、およびその情報を取得する方法をパラメーター化する型 Left と Right についての情報をエンコードできるかどうかを知りたいです。

ほとんどが c++11 準拠の gcc 4.8.5 をサポートする必要があります。

update 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{} );
}

4

1 に答える 1

1

左辺値/右辺値情報をLeftおよびRight型にエンコードできます。例えば:

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_;
};

または、明示的に指定して、Scalars を除くすべてを値で格納することもできます。by 値を格納できるようにするに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 機能をいくつか使用しました。)

于 2019-06-16T17:16:06.910 に答える