当前位置: 动力学知识库 > 问答 > 编程问答 >

c++ - unititialized copy via CRTP pattern passed by value

问题描述:

I'm using a CRTP pattern, and trying to define operators that apply to its implementations. I've discovered a strange behavior of uninitialized objects.

CRTP base class:

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

};

Implementation 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;

};

Implementation 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;

};

Operators (operator! works fine, operator~ shows the problem):

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())); }

Now with a simple main:

int main(void)

{

auto n = -Impl(10);

n.printValue();

n = ~Impl(20);

n.printValue();

}

Compiled with gcc, I have:

CRTP value : -10

CRTP value : 0

Compiled with CLANG I get:

CRTP value : -10

CRTP value : -1186799704

Now I have 2 questions :

  • is this behavior standard ? It happens even if I delete the default

    constructors.

  • how to implement the operators in the "passed by value

    style" ? I have n-ary operators that I want to implement using

    variadic templates, and I can't enumerate every combination of lvalue and rvalue references.

网友答案:

You're slicing your objects in the parameter of operator ~. You're taking CRTP<C> by value, which means anything beyond the CRTP subobject (such as the members v or a) will be sliced away. You're then invoking Undefined Behaviour by casting the sliced object of type CRTP<C> to type C, which it is not.

When you pass by value, you need to pass by value of the correct type—the type which the object actually is. Your objects are not of type CRTP<C> for any C, they are of a type derived from CRTP<C> (for some C).

If you want to keep the pass-by-value signature, you'll have to accept anything and check for the presence of the correct base class with SFINAE:

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())); }

Or you could use perfect forwarding, with the same trick.

分享给朋友:
您可能感兴趣的文章:
随机阅读: