我正在使用CRTP模式,并尝试定义适用于其实现的运算符。我发现了未初始化对象的奇怪行为。
CRTP基类:
template < class C >
struct CRTP
{
using self_t = C;
const self_t& self() const
{ return static_cast<const self_t&>(*this); }
self_t& self()
{
const CRTP& cs = static_cast<const CRTP&>(*this);
return const_cast<self_t&>(cs.self());
}
void printValue()
{ cout << "CRTP value : " << self().getValue() << endl; }
};
实施1:
struct Impl : public CRTP<Impl> {
Impl() = default;
Impl(Impl&&) = default;
Impl(const Impl&) = default;
explicit
Impl(int i) : v(i) { }
friend void swap(Impl& l, Impl& r)
{ using std::swap; swap(l.v, r.v); }
Impl& operator=(Impl o)
{ swap(*this, o); return *this; }
int getValue() const
{ return v; }
private:
int v;
};
实施2:
template < class Arg >
struct Neg : public CRTP< Neg<Arg> >
{
Neg() = default;
Neg(Neg&&) = default;
Neg(const Neg&) = default;
explicit
Neg(Arg arg) : a(arg) { }
friend void swap(Neg& l, Neg& r)
{ using std::swap; swap(l.a, r.a); }
Neg& operator=(Neg o)
{ swap(*this, o); return *this; }
int getValue() const
{ return -a.getValue(); }
private:
Arg a;
};
运算符(operator!
正常,operator~
显示问题):
template < class C >
Neg<C> operator~(CRTP<C> v)
{ return Neg<C>(std::move(v.self())); }
template < class C >
Neg<C> operator-(const CRTP<C>& v)
{ return Neg<C>(v.self()); }
template < class C >
Neg<C> operator-(CRTP<C>&& v)
{ return Neg<C>(std::move(v.self())); }
现在有了一个简单的主要内容:
int main(void)
{
auto n = -Impl(10);
n.printValue();
n = ~Impl(20);
n.printValue();
}
使用gcc编译,我有:
CRTP value : -10
CRTP value : 0
用CLANG编译我得到:
CRTP value : -10
CRTP value : -1186799704
现在我有两个问题:
答案 0 :(得分:5)
您正在CRTP<C>
参数中对对象进行切片。您按价值计算CRTP
,这意味着v
子对象之外的任何内容(例如成员a
或CRTP<C>
)都会被切掉。然后,您通过将类型为C
的切片对象强制转换为CRTP<C>
来调用未定义的行为,而不是。{/ p>
当您通过值传递时,您需要传递正确类型的值 - 对象实际所在的类型。对于任何C
,您的对象不属于CRTP<C>
类型,它们属于来自C
的派生类型(对于某些template <class C>
typename std::enable_if<std::is_base_of<CRTP<C>, C>::value, Neg<C>>::type operator~ (C v)
{ return Neg<C>(std::move(v.self())); }
)。
如果您想保留按值传递签名,您必须接受任何内容并使用SFINAE检查是否存在正确的基类:
{{1}}
或者你也可以使用完美的转发技巧。