locked
problem with vector RRS feed

  • Question

  • Hi all i am using following code with vector

    class myc
    {
    	static int i;
    public:
    	myc()
    	{
    		cout<<++i<<" ctor \n";
    	}
    	~myc()
    	{
    		cout<<--i<<" dtor \n";
    	}
    };
    
    int myc::i = 0;
    
    int main()
    {
    	{
    		vector<myc> vec(5);
    	}
    	return 0;
    }

    and got output

    1 ctor
    0 dtor
    1 ctor
    0 dtor
    1 ctor
    0 dtor
    1 ctor
    0 dtor
    1 ctor
    0 dtor
    -1 dtor
    -2 dtor
    -3 dtor
    -4 dtor
    -5 dtor
    Press any key to continue . . .

    why i got this output as per c++ 11 no copying should be done

    thanks in advance here is  oreginal post

    Monday, February 20, 2012 11:57 AM

Answers

  • This is down to the implementation of vector itself and not the compiler. If you have any issues with this then submit a suggestion on the connect website. But one thing to note, this isn't a bug.

    This particular constructor is not required to not make copies, all internal members that this implementation calls are also allowed to make copies. If you are having problems because of this then it is really just exposing a weakness in your class design anyway.

    If this behaviour is causing problems for your class then it means that it will be causing problems in a situation where the trivial constructor is not sufficient for what you want to do. For example, if you are dealing with pointers and memory allocated using new, then you are going to have to problems. This problem has been known for a long time, long enough for someone to come up with a snappy name for it. So you should look at the Rule of three.

    The same will be true with the move constructor, lets face it, how does the compiler know which members should be moved and those which shouldn't. How does it know the difference between pointer A which contains all of the data for the class and that means that it can be safely moved to the new class and set to null, and pointer B which is a pointer to a parent class and if it does get moved then it will end up with the destructor failing.

    So basically, unless your class is made up entirely of value types and trivial members you will need to write any copy/move constructors yourself. The defaults only do a shallow copy after all.


    This is a signature

    Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Proposed as answer by Helen Zhao Wednesday, February 22, 2012 2:29 AM
    • Marked as answer by Helen Zhao Friday, March 2, 2012 1:29 AM
    Monday, February 20, 2012 1:52 PM
  • When you add copy constructor, you get the following output:

    1 ctor 
    2 copy ctor 
    1 dtor 
    2 ctor 
    3 copy ctor 
    2 dtor 
    3 ctor 
    4 copy ctor 
    3 dtor 
    4 ctor 
    5 copy ctor 
    4 dtor 
    5 ctor 
    6 copy ctor 
    5 dtor 
    4 dtor 
    3 dtor 
    2 dtor 
    1 dtor 
    0 dtor 

    When you add move ctor, you get the following output:

    1 ctor 
    2 move ctor 
    1 dtor 
    2 ctor 
    3 move ctor 
    2 dtor 
    3 ctor 
    4 move ctor 
    3 dtor 
    4 ctor 
    5 move ctor 
    4 dtor 
    5 ctor 
    6 move ctor 
    5 dtor 
    4 dtor 
    3 dtor 
    2 dtor 
    1 dtor 
    0 dtor 

    Which is what you expect probably. The final sample code is:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class myc
    {
    	static int i;
    public:
    	myc()
    	{
    		cout<<++i<<" ctor \n";
    	}
    	
    	myc(myc const &)
    	{
    		cout<<++i<<" copy ctor \n";
    	}
    	
    	myc(myc &&)
    	{
    		cout<<++i<<" move ctor \n";
    	}
    	
    	~myc()
    	{
    		cout<<--i<<" dtor \n";
    	}
    };
    
    int myc::i = 0;
    
    int main()
    {
    	{
    		vector<myc> vec(5);
    	}
    	return 0;
    }

    Toni Petrina
    My blog: Programming ramblings
    If a post answers your question, please click "Mark As Answer" on that post and "Vote as Helpful"

    • Proposed as answer by Helen Zhao Wednesday, February 22, 2012 2:29 AM
    • Marked as answer by Helen Zhao Friday, March 2, 2012 1:30 AM
    Monday, February 20, 2012 1:22 PM

All replies

  • When you add copy constructor, you get the following output:

    1 ctor 
    2 copy ctor 
    1 dtor 
    2 ctor 
    3 copy ctor 
    2 dtor 
    3 ctor 
    4 copy ctor 
    3 dtor 
    4 ctor 
    5 copy ctor 
    4 dtor 
    5 ctor 
    6 copy ctor 
    5 dtor 
    4 dtor 
    3 dtor 
    2 dtor 
    1 dtor 
    0 dtor 

    When you add move ctor, you get the following output:

    1 ctor 
    2 move ctor 
    1 dtor 
    2 ctor 
    3 move ctor 
    2 dtor 
    3 ctor 
    4 move ctor 
    3 dtor 
    4 ctor 
    5 move ctor 
    4 dtor 
    5 ctor 
    6 move ctor 
    5 dtor 
    4 dtor 
    3 dtor 
    2 dtor 
    1 dtor 
    0 dtor 

    Which is what you expect probably. The final sample code is:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class myc
    {
    	static int i;
    public:
    	myc()
    	{
    		cout<<++i<<" ctor \n";
    	}
    	
    	myc(myc const &)
    	{
    		cout<<++i<<" copy ctor \n";
    	}
    	
    	myc(myc &&)
    	{
    		cout<<++i<<" move ctor \n";
    	}
    	
    	~myc()
    	{
    		cout<<--i<<" dtor \n";
    	}
    };
    
    int myc::i = 0;
    
    int main()
    {
    	{
    		vector<myc> vec(5);
    	}
    	return 0;
    }

    Toni Petrina
    My blog: Programming ramblings
    If a post answers your question, please click "Mark As Answer" on that post and "Vote as Helpful"

    • Proposed as answer by Helen Zhao Wednesday, February 22, 2012 2:29 AM
    • Marked as answer by Helen Zhao Friday, March 2, 2012 1:30 AM
    Monday, February 20, 2012 1:22 PM
  • This is down to the implementation of vector itself and not the compiler. If you have any issues with this then submit a suggestion on the connect website. But one thing to note, this isn't a bug.

    This particular constructor is not required to not make copies, all internal members that this implementation calls are also allowed to make copies. If you are having problems because of this then it is really just exposing a weakness in your class design anyway.

    If this behaviour is causing problems for your class then it means that it will be causing problems in a situation where the trivial constructor is not sufficient for what you want to do. For example, if you are dealing with pointers and memory allocated using new, then you are going to have to problems. This problem has been known for a long time, long enough for someone to come up with a snappy name for it. So you should look at the Rule of three.

    The same will be true with the move constructor, lets face it, how does the compiler know which members should be moved and those which shouldn't. How does it know the difference between pointer A which contains all of the data for the class and that means that it can be safely moved to the new class and set to null, and pointer B which is a pointer to a parent class and if it does get moved then it will end up with the destructor failing.

    So basically, unless your class is made up entirely of value types and trivial members you will need to write any copy/move constructors yourself. The defaults only do a shallow copy after all.


    This is a signature

    Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Proposed as answer by Helen Zhao Wednesday, February 22, 2012 2:29 AM
    • Marked as answer by Helen Zhao Friday, March 2, 2012 1:29 AM
    Monday, February 20, 2012 1:52 PM
  • Hi vivek_mendse,

    Thank you for your active participation in the MSDN Forum.

    I agree with Crescrens2k. You can submit this suggestion or feedback to Microsoft Connect feedback Center in formal format. Microsoft engineers will evaluate them seriously and report to Product Group.

    In your feedback, you can quote the URL to this thread, so that the whole conversation can be available. Every feedback submitted will be evaluated carefully by our engineers. They will let you know their comments further through that portal. It would be great if you can also paste the link to the submitted feedback here, so that other community members can see it as well.

    Thank you again for your valuable feedback!
    Have a nice day!


    Helen Zhao [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, February 22, 2012 2:34 AM
  • i am not sure upto what extent c++ 11 is supported in vs 2010.

    here my point is c++ 11 implementation (ofcourse shallow copy will cause problem with pointers in any class/container) 

    as per c++ 11 vector implementation see link

    note 3'rd poin

    also see gcc compiler / vector implementation quite different ( i think its MINGW)

    if below statement is true for c++ iso standard

    explicit vector ( size_type n, const T& value= T(), const Allocator& = Allocator() );
    Repetitive sequence constructor: Initializes the vector with its content set to a repetition, n times, of copies of value.

    MINGW seems to be close to standard.

    i am trying to find out things myself and will send my view to microsoft.


    • Edited by vivek_mendse Wednesday, February 22, 2012 5:25 AM
    Wednesday, February 22, 2012 5:18 AM
  • >i am not sure upto what extent c++ 11 is supported in vs 2010.

    See:

    C++0x Core Language Features In VC10: The Table
    http://blogs.msdn.com/b/vcblog/archive/2010/04/06/c-0x-core-language-features-in-vc10-the-table.aspx

    Also see:

    C++11 Features in Visual C++ 11
    http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx

    - Wayne
    Wednesday, February 22, 2012 6:59 AM
  • Well, for the standard itself, all it has to say about both of the size versions of vector is:

    explicit vector(size_type n);
    3 Effects: Constructs a vector with n value-initialized elements.
    4 Requires: T shall be DefaultConstructible.
    5 Complexity: Linear in n.

    vector(size_type n, const T& value,
    const Allocator& = Allocator());
    6 Effects: Constructs a vector with n copies of value, using the specified allocator.
    7 Requires: T shall be CopyInsertable into *this.
    8 Complexity: Linear in n.

    so can you tell me how the gcc implementation is closer to the standard when you didn't even use the standard as reference. Also, from what I have read, the standard doesn't give how many times that that a value myst be copied. Looking at the requirements the standard doesn't give any details on how the vector must initialise itself for this, all it does give is that it must initialise and the object itself must be default constructable, also it must also construct the variable using a certain member from the allocator.

    Anyway, the important part here is the difference in the implementations. First, GCC constructs one temporary and they copies it to each element. So gcc does construct and copy (if you want to dispute that, please don't, your own output in your original post shows this

    1 base ctor 
    0 base dtor 
    -1 base dtor 
    -2 base dtor 
    -3 base dtor 
    -4 base dtor 
    -5 base dtor 

    the ctor call followed by the dtor call shows that it was only construced once, to get the other 5 instances of the class means that it would have to go through the copy or move constructor since your original code didn't have either of those members.) Now, this copies much like VC, the only difference is the extent of the copying. So by what you said, GCC is also going against what you linked is saying.

    Anyway, what VC does is to construct n temporary objects and then moves them into the vector where possible (in the following the mctor is the move constructor, that is the constructor with the definition of myclass(myclass&&).

    1 ctor
    2 mctor
    1 dtor
    2 ctor
    3 mctor
    2 dtor
    3 ctor
    4 mctor
    3 dtor
    4 ctor
    5 mctor
    4 dtor
    5 ctor
    6 mctor
    5 dtor
    4 dtor
    3 dtor
    2 dtor
    1 dtor
    0 dtor

    So this works in a similar fasion to the GCC implementation, construct then copy/move. So if what you say is true, then both implementations break the standard, but to prove that they break the standard you would have to state the section and paragraph in the standard that says the size version of the constructor can't make copies. So don't give some reference as fact, give references to the C++ 11 standard.

    For the size, T, allocator version of the constructor, I have no idea what your problem with that is. The VC implementation of vector does exactly what the standard says, it takes in the value and then copy constructs into the vector itself so exactly n + 1 instances are created of your class.

    1 ctor
    2 cctor
    3 cctor
    4 cctor
    5 cctor
    6 cctor
    5 dtor
    4 dtor
    3 dtor
    2 dtor
    1 dtor
    0 dtor

    So again I really can't see any issues. In fact, I'm actually thinking that GCC is using this constructor, or sharing code between both of the size constructors because their implementation can be the same.

    I have read through the standard section 23.2 and 23.3, and besides the requirements for it saying that the class must be default constructable, the standard doesn't mention anything else. (I do see the problem with this, to some the default constructable requirement hints that it will default construct them in place).

    So I would suggest you go back to GCC, fill out your class properly and check to see how it handles your first sample with user copy and move constructors available, you should notice that it does copy or move. But still, if you are unhappy then as I said before, file a suggestion on the connect website saying you feel that you feel that theri interpretation of the standard is wrong and give really good reasons why. If you can, also try to test this out on the VC11 developer preview since that is the current version in development. You can see if what you want done has already been changed to your liking.


    This is a signature

    Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Wednesday, February 22, 2012 1:11 PM
    Wednesday, February 22, 2012 1:03 PM
  • thanks for reply

    as i said >  i am trying to find out things myself and will send my view to microsoft.

    let me continue on same. aslo let me try in vc 11 ( well i never say vs is wrong  every library will be different from each other.)

    Thursday, February 23, 2012 6:05 AM
  • Hi vivek_mendse,

    I am checking the thread status on my side. Would you mind letting me know the result of the suggestions? If the issue has been resolved, please mark the reply which helps you as answer. Or please post your solution here and share it with us if you have find it by yourself. It is beneficial for other community members who have similar questions.

    Good Luck!
    Helen Zhao


    Helen Zhao [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, February 28, 2012 1:32 AM