views:

169

answers:

2

If so, why? Why doesn't it use the copy constructor of the value type?

I get the following error:

/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/vector.tcc: In member functio
n `ClassWithoutAss& ClassWithoutAss::operator=(const ClassWithoutAss&)':
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/vector.tcc:238:   instantiate
d from `void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterato
r<typename _Alloc::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp =
ClassWithoutAss, _Alloc = std::allocator<ClassWithoutAss>]'
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/stl_vector.h:564:   instantia
ted from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = Class
WithoutAss, _Alloc = std::allocator<ClassWithoutAss>]'
main.cpp:13:   instantiated from here
/usr/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/bits/vector.tcc:238: error: non-st
atic const member `const int ClassWithoutAss::mem', can't use default assignment
 operator

running g++ main.cpp on the following code:

/*
 * ClassWithoutAss.h
 *
 */

#ifndef CLASSWITHOUTASS_H_
#define CLASSWITHOUTASS_H_

class ClassWithoutAss
{

public:
    const int mem;
    ClassWithoutAss(int mem):mem(mem){}
    ClassWithoutAss(const ClassWithoutAss& tobeCopied):mem(tobeCopied.mem){}
    ~ClassWithoutAss(){}

};

#endif /* CLASSWITHOUTASS_H_ */

/*
 * main.cpp
 *
 */

#include "ClassWithoutAss.h"
#include <vector>

int main()
{
    std::vector<ClassWithoutAss> vec;
    ClassWithoutAss classWithoutAss(1);
    (vec.push_back)(classWithoutAss);

    return 0;
}
+8  A: 

The C++03 standard says elements must be copy-constructible and copy-assignable to be used in a standard container. So an implementation is free to use whichever it wants.

In C++0x, these requirements are put on a per-operation basis. (In general, elements must be move-constructible and move-assignable.)

To get what you want, you should use a smart pointer like shared_ptr (from either Boost, TR1, or C++0x), and completely disable copy-ability:

class ClassWithoutAss
{
public:
    const int mem;

    ClassWithoutAss(int mem):mem(mem){}
    // don't explicitly declare empty destructors

private:
    ClassWithoutAss(const ClassWithoutAss&); // not defined
    ClassWithoutAss& operator=(const ClassWithoutAss&); // not defined
};

typedef shared_ptr<ClassWithoutAss> ptr_type;

std::vector<ptr_type> vec;
vec.push_back(ptr_type(new ClassWithoutAss(1)));

Pointers can be copied just fine, and the smart pointer ensures you don't leak. In C++0x you can do this best with a std::unique_ptr, taking advantage of move-semantics. (You don't actually need shared semantics, but in C++03 it's easiest as it stands.)

GMan
I believe you, but can you explain why pointers rather than defining my own operations? Faster push_backs? So I don't waste my time defining operations? I'll have to look into move/share semantics. Thanks GMan. I only have these problems with your help. ;)
@drenami: What do you mean? I used pointers because you want to have your class in the container, but you can't do it directly. One abstraction above that is a pointer to your class, rather than the class itself. (And the smart pointers just to prevent leaks.)
GMan
But I could if I defined an assignment operator right? So my question is pro/cons of the two designs--one without assignment/using pointers, one defining assignment. Both are options because I can write ClassWithAss.
@drenami: I see. The thing is, your assignment operator would have to just do nothing, which is about as far from "assigning" as it gets. While there are certainly ways to hack around the problem, the more correct solution would be to recognize the design consequences. That is, if a class has a const member, it doesn't make conceptual sense to copy or assign it; that's why I recommend disabling those operations entirely. However, another way to go is to get rid of the const part, allowing real assignments to occur. (Obviously this choice depends on your big picture, which I don't know.)
GMan
+2  A: 

The problem here is that types in a container must be assignable.

Because you do not define an assignment operator for your class the compiler will generate one for you. The default assignment operator will look like this:

ClassWithoutAss& operator=(ClassWithoutAss const& rhs)
{
    mem = copy.mem;
    return *this;
}
// The compiler generated assignemtn operator will copy all members
// using that members assignment operator.

In most situations this would work. But the member mem is a const and thus unassignable. Therefore compilation will fail when it tries to generate the assignment operator.

Martin York