Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


[C++0x] "canonical" class - copy/move construction/assignment


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
16 replies to this topic

#1 Koen   Members   -  Reputation: 511

Like
0Likes
Like

Posted 19 April 2010 - 11:07 AM

Since VS2010 has some Cpp0x features implemented, I thought it was about time to start learning about them. Rvalue references will probably have the largest impact on how one should write classes. So I'm trying to find some "standard" way of implementing a class. Something that replaces "implement a non-throwing swap and implement assignment in function of copy construction". My current understanding leads to this:
class Foo
{
private:
    State m_state; //assume State to be a 'canonical' class

public:
    Foo()
    {
        //...    
    }

    Foo(const Foo& other):
        m_state(other.m_state)
    {
    }

    Foo(Foo&& other):
        m_state(std::forward<State>(other.m_state))
    {
    }

    Foo& operator = (const Foo& other)
    {
        Foo temp(other);
        *this = std::move(temp);
        return *this;
    }

    Foo& operator = (Foo&& other)
    {
        m_state = std::move(other.m_state);
        return *this;
    }
};

Does that seem ok? The move constructor and move assignment are kindof doing the same thing, but any attempts to use swap to fix this have lead to infinite loops (as std::swap uses operator = (Type&&)). Is there a way to fix this? I'm also not too sure about my use of std::move and std::forward. Different texts seem to contradict each other on this issue. Any comments or links would be much appreciated.

Sponsor:

#2 no such user   Members   -  Reputation: 280

Like
0Likes
Like

Posted 19 April 2010 - 12:37 PM

Why are you using std::forward() in the move constructor? And why not write your own swap function that swaps the member data?

class Foo {
private:
State m_state; //assume State to be a 'canonical' class
public:
Foo() {}
Foo(const Foo & other) : m_state(other.m_state) {}
Foo(Foo && other) : m_state(std::move(other.m_state)) {}

Foo & operator=(const Foo & other) {
Foo(other).swap(*this);
return *this;
}

Foo & operator=(Foo && other) {
Foo(std::move(other)).swap(*this);
return *this;
}

void swap(Foo & other) {
using std::swap; // use ADL to lookup swap in State's namespace or
swap(m_state, other.m_state); // fallback on std::swap if no such function
}
};

void swap(Foo & lhs, Foo & rhs) {
lhs.swap(rhs);
}


#3 gekko   Members   -  Reputation: 478

Like
0Likes
Like

Posted 19 April 2010 - 07:02 PM

As already mentioned, you should be using std::move in the move constructor and move assignment operator. Forward is for perfect forwarding, which is a completely different concept.

You need to be careful with handling assignment to self with move assignment as well as exception safety. Not so sure I like the idea of implementing copy assignment in terms of move assignment. It's actually causing more copies that way.

Assuming a member is movable, you're copying it into temp, then moving it into *this. If the member is not movable, you're copying it once into temp, then copying it again in the move assignment. In either case, it would be more efficient to simply copy once and be done with it.
-- gekko

#4 DevFred   Members   -  Reputation: 836

Like
0Likes
Like

Posted 19 April 2010 - 10:26 PM

If you don't write your own copy constructor, assignment operator and destructor, the move constructor and move assignment operator will be generated automatically. So as long as you don't use pointers as members (and you rarely should), you will be fine.

#5 Koen   Members   -  Reputation: 511

Like
0Likes
Like

Posted 19 April 2010 - 11:25 PM

Ah, I misunderstood some post I read before. It shows a templatized move constructor where the author uses std::forward instead of std::move. But that's a different case.
Quote:
Original post by DevFred
If you don't write your own copy constructor, assignment operator and destructor, the move constructor and move assignment operator will be generated automatically. So as long as you don't use pointers as members (and you rarely should), you will be fine.

I think this is wrong... See MS Visual C++ blog
Quote:
Some microsoft visual studio guy
At this point, you may be wondering how this interacts with automatically generated ("implicitly declared" in Standardese) constructors and assignment operators.
  • Move constructors and move assignment operators are never implicitly declared.
  • The implicit declaration of a default constructor is inhibited by any user-declared constructors, including copy constructors and move constructors.
  • The implicit declaration of a copy constructor is inhibited by a user-declared copy constructor, but not a user-declared move constructor.
  • The implicit declaration of a copy assignment operator is inhibited by a user-declared copy assignment operator, but not a user-declared move assignment operator.

  • So I tested it using no such user's Foo class and the following Bar class


    class Bar
    {
    private:
    Foo m_foo;
    };

    Bar BarFactory()
    {
    Bar newBar;
    return newBar;
    }



    Using the BarFactory function triggers foo's copy constructor, not the move constructor. And I doubt this is a Visual Cpp bug...

    Thanks for all your replies!

    #6 DevFred   Members   -  Reputation: 836

    Like
    0Likes
    Like

    Posted 19 April 2010 - 11:33 PM

    Quote:
    Original post by Koen
    Quote:
    Original post by DevFred
    If you don't write your own copy constructor, assignment operator and destructor, the move constructor and move assignment operator will be generated automatically. So as long as you don't use pointers as members (and you rarely should), you will be fine.

    I think this is wrong...

    When it comes to C++, I trust Bjarne more than MS ;-)

    [Edited by - DevFred on April 20, 2010 11:33:15 AM]

    #7 Koen   Members   -  Reputation: 511

    Like
    0Likes
    Like

    Posted 20 April 2010 - 04:00 AM

    Quote:
    Original post by DevFred
    When it comes to C++, I trust Bjarne more than MS ;-)

    I've checked with the latest standard draft, and it agrees with you :-)
    Quote:
    From the draft standard March 2010, section 12.8.10, p273

    If the class definition does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
  • X does not have a user-declared copy constructor and
  • the move constructor would not be implicitly defined as deleted.

  • Bummer. I had assumed microsoft would have made the features it already implemented standard compliant.
    Little bit off topic, but are there other compilers that already implement this stuff properly (or any 0x stuff for that matter)? It seems gcc or intel don't implement this specific one either...



    #8 gekko   Members   -  Reputation: 478

    Like
    0Likes
    Like

    Posted 20 April 2010 - 05:22 AM

    Quote:
    Original post by Koen
    Bummer. I had assumed microsoft would have made the features it already implemented standard compliant.
    Little bit off topic, but are there other compilers that already implement this stuff properly (or any 0x stuff for that matter)? It seems gcc or intel don't implement this specific one either...


    There is no "properly" since the standard isn't finished. Also, do you realize the blog post you reference is 4 months older than the proposal to add implicit moves? After that, it takes time to approve these things and add them in the standard. At the time, the article was correct, but the standard changes.

    Microsoft's implementation in VS2010 is actually different than that post as well. You can see some of the changes here:

    http://blogs.msdn.com/vcblog/archive/2010/04/06/c-0x-core-language-features-in-vc10-the-table.aspx
    -- gekko

    #9 Koen   Members   -  Reputation: 511

    Like
    0Likes
    Like

    Posted 20 April 2010 - 06:03 AM

    Quote:
    Original post by gekko
    There is no "properly" since the standard isn't finished.

    I understand that, but I didn't think it was still changing that much. I was under the impression that the current draft is more or less final. But apparently it will certainly not be final before 2011.
    I wish it was 2012 already :-)
    Quote:
    Original post by gekko
    Microsoft's implementation in VS2010 is actually different than that post as well. You can see some of the changes here

    Thanks!



    #10 SiCrane   Moderators   -  Reputation: 9624

    Like
    0Likes
    Like

    Posted 20 April 2010 - 06:58 AM

    Quote:
    Original post by DevFred
    When it comes to C++, I trust Bjarne more than MS ;-)

    Yeah, and according to the C++ standard, you can use export with templates. You need to face reality and deal with the limitations of the tools you are using. Saying that you should write your code as if your compiler implements the language "correctly" in face of the fact that your compiler doesn't implement the language that way is just idiotic.

    #11 visitor   Members   -  Reputation: 643

    Like
    0Likes
    Like

    Posted 20 April 2010 - 07:04 AM

    It's also good news in that draft that if you needed to define the move operators, it would look like this:


    Foo(Foo&&) = default;
    Foo& operator=(Foo&&) = default;


    #12 DevFred   Members   -  Reputation: 836

    Like
    0Likes
    Like

    Posted 20 April 2010 - 08:23 AM

    Quote:
    Original post by SiCrane
    Yeah, and according to the C++ standard, you can use export with templates.

    There is no C++0x standard yet, and the VS Support is experimental. I expect VS to eventually catch up. It's not like automatically generating move constructors and move assignment operators takes 100 man-years ;-)

    Quote:
    Original post by SiCrane
    Saying that you should write your code as if your compiler implements the language "correctly" in face of the fact that your compiler doesn't implement the language that way is just idiotic.

    Koen ultimately wants to know how to write classes in the future when C++0x is actually real, right? It's not about getting the full power of rvalue references with current tools:
    Quote:

    I thought it was about time to start learning about them. Rvalue references will probably have the largest impact on how one should write classes.



    #13 SiCrane   Moderators   -  Reputation: 9624

    Like
    0Likes
    Like

    Posted 20 April 2010 - 11:08 AM

    There's no good reason to deliberately write less performant code just to satisfy some ivory tower intellectual exercise about how code "should" be written when the increasingly misnamed C++0x standard is finally delivered. Maybe in five years not defining your move constructors will be good advice. However, it doesn't take a hundred man years to manually write out a move constructor either.

    #14 Koen   Members   -  Reputation: 511

    Like
    0Likes
    Like

    Posted 20 April 2010 - 11:15 AM

    Quote:
    Original post by SiCrane
    Saying that you should write your code as if your compiler implements the language "correctly" in face of the fact that your compiler doesn't implement the language that way is just idiotic.

    Not necessarily. If you're working with a large codebase, it's always nice to be able to upgrade your tools without too much effort. Living by the future rules of your compiler - currently not enforced - would be a wise thing to do. (This particular example is the other way around, of course: it's about an optimization that's not there yet).
    In one of the above links it is claimed that MS employees call VC10 "the new VC6". Where I work, people still have nightmares about migrating code from VC6 to 7 and 8. Fortunately I was not around in those days :-)
    Quote:
    Original post by DevFred
    Koen ultimately wants to know how to write classes in the future when C++0x is actually real, right? It's not about getting the full power of rvalue references with current tools:

    Yes. And before this thread, I believed the two to be the same ;-)
    Quote:
    Original post by SiCrane
    However, it doesn't take a hundred man years to manually write out a move constructor either.

    True. In this case it only costs a little time. And the resulting code will still be useful once move constructor/assignment are generated by the compiler.


    #15 DevFred   Members   -  Reputation: 836

    Like
    0Likes
    Like

    Posted 20 April 2010 - 11:23 AM

    Quote:
    Original post by SiCrane
    There's no good reason to deliberately write less performant code

    Yes there is, compatibility. As soon as you write your own move constructors, C++03 compilers will choke on your code. Of course you can use conditional compilation, but that doesn't make the code any more readable. And once the compilers support the automation, your code is full of irrelevant noise.

    What does the latest GCC say to this code?

    #include <iostream>

    struct Foo
    {
    Foo()
    {
    std::cout << "Foo default\n";
    }

    Foo(const Foo&)
    {
    std::cout << "Foo copy\n";
    }

    Foo(Foo&&)
    {
    std::cout << "Foo move\n";
    }
    };

    struct Bar
    {
    Foo foo;
    };

    Foo foo()
    {
    return Foo();
    }

    Bar bar()
    {
    return Bar();
    }

    int main()
    {
    foo();
    bar();
    }


    #16 visitor   Members   -  Reputation: 643

    Like
    0Likes
    Like

    Posted 21 April 2010 - 04:37 AM

    I have 4.4.1 and it says:


    Foo default
    Foo default


    :)

    With -fno-elide-constructors it says:


    Foo default
    Foo move
    Foo default
    Foo copy


    #17 DevFred   Members   -  Reputation: 836

    Like
    0Likes
    Like

    Posted 21 April 2010 - 09:35 AM

    Quote:
    Original post by visitor
    Foo copy

    Bummer :(




    Old topic!
    Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



    PARTNERS