4

次のようなCRTP基本クラスがあります。

template<typename Derived, size_t DIMS>
class Base {
public:
    // here is I think where the problem is
    inline const Derived& self() const {return *static_cast<const Derived*>(this);}
};

次に、派生クラスは次のように定義されます

template<typename T, size_t ... Rest>
class Derived: public Base<Derived<T,Rest...>,sizeof...(Rest)> {
public:

    Derived() = default;

    // This constructor binds any arbitrary expression to Derived
    template<typename Expr, size_t DIMS>
    inline Derived(const Base<Expr,DIMS>& src_) {
        const Expr &src = src_.self();
        print(src.rhs);
    }
};

独自の演算子を定義することを念頭に置いて、AddOperatorベースから継承する次のものもあります

template<typename TLhs, typename TRhs, size_t DIMS>
struct AddOperator: public Base<AddOperator<TLhs, TRhs, DIMS>,DIMS> {
    AddOperator(const TLhs& lhs, const TRhs& rhs) : lhs(lhs), rhs(rhs) {
        print(rhs);
    }
    const TLhs &lhs;
    const TRhs &rhs;
};

次に、型とプリミティブ型のoperator+間のオーバーロードは、ある種のプロキシ/式のみを返します。Derived

template<typename TLhs, typename TRhs, size_t DIM0,
         typename std::enable_if<!std::is_arithmetic<TLhs>::value &&
                                 std::is_arithmetic<TRhs>::value,bool>::type = 0 >
inline AddOperator<TLhs, TRhs, DIM0> 
operator+(const Base<TLhs,DIM0> &lhs, TRhs rhs) {
  return AddOperator<TLhs, TRhs, DIM0>(lhs.self(), rhs);
}

ただし、これを下で呼び出すと、 of のclangガベージ値が取得されます。次に例を示します。rhsAddOperator

int main() {

    Derived<double,2,2> g;
    Derived<double,2,2> x = g+28;

    return 0;
}

lhsrhsinの両方AddOperatorが型である場合の他のオーバーロードには、Derivedこの問題はありません。

この問題は、 の下でのみ発生しclangます。gccコンパイルされたコードは正常に動作するようです。問題がどこにあるか誰か知っていますか?

完全なデモはこちら

4

1 に答える 1

6

Your problem is that you have a dangling reference.

In operator+, you take a TRhs (int) by value and then construct an AddOperator<...> with a reference to it. When g+28 returns, the AddOperator<...> object still has a reference to the parameter rhs - whose lifetime has now ended.

The garbage you're printing is the outcome of accessing a destroyed object - it's undefined behavior. On clang, this manifests itself as printing a garbage value for you, on gcc it happens to work. Undefined behavior is tricky like that.


Now, the seemingly "obvious" fix is to change operator+ to take rhs by reference to const. This will extend the lifetime of the temporary 28 through the end of the full expression containing the call - so now your print statements are guaranteed to work. At least, until the end of the line. After x is constructed, the reference will dangle yet again.

于 2016-05-25T15:36:16.113 に答える