我有一个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,它也从base继承
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);
}

但是,当我在clang下调用它时,我得到rhsAddOperator的垃圾值。这是一个例子:
int main() {

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

    return 0;
}

lhs中的rhsAddOperator均为Derived类型时,其他重载都不会出现此问题。

仅在clang下会发生此问题。 gcc编译的代码似乎运行良好。有人知道问题出在哪里吗?

Full Demo Here

最佳答案

您的问题是您有一个悬而未决的引用。

operator+中,您可以按值获取TRhs(int),然后使用对其进行引用来构造AddOperator<...>。当g+28返回时,AddOperator<...>对象仍然具有对rhs参数的引用-该参数的生命周期现已结束。

您正在打印的垃圾是访问被破坏对象的结果-这是未定义的行为。在clang上,这表现为为您打印垃圾值,在gcc上它确实起作用。这样,未定义的行为很棘手。

现在,看似“显而易见”的解决方案是通过引用operator+rhs更改为采用const。这会将临时28的生命周期延长到包含该调用的完整表达式的末尾-因此现在可以保证您的打印语句正常工作。至少直到行尾。构造x后,引用将再次悬挂。

09-06 13:34