Why constant objects can't be replaced in a container? [duplicate] - c++

How to push_back() to a C++ std::vector without using operator=() for which the default definition violates having const members?
struct Item {
Item(int value)
: _value(value) {
}
const char _value;
}
vector<Item> items;
items.push_back(Item(3));
I'd like to keep the _value const since it should not change after the object is constructed, so the question is how do I initialize my vector with elements without invoking operator=()?
Here is the basic error the g++ v3.4.6 is giving me:
.../3.4.6/bits/vector.tcc: In member function `Item& Item::operator=(const Item&)':
.../3.4.6/bits/vector.tcc:238: instantiated from `void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename _Alloc::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp = Item, _Alloc = std::allocator<Item>]'
.../3.4.6/bits/stl_vector.h:564: instantiated from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = Item, _Alloc = std::allocator<Item>]'
item.cpp:170: instantiated from here
.../3.4.6/bits/vector.tcc:238: error: non-static const member `const char Item::_value', can't use default assignment operator

For std::vector<T> the elements are required to be Assignable. You type is not Assignable. An implementation of. std::vector<T> could avoid insisting on this requirement but this would be a disservice as the resulting code wouldn't be portable.
You can use a std::list<T> instead or change the definition of you type. For example you can create an accessor to only read the value but no setter. Of course, assignment would change the value. The choice thus is to either allow this change or allow putting the objects into a container. You won't get both.

Related

What are the requirements for implementing an allocator with a fancy pointer which works with STL containers?

I am attempting to implement an Allocator which allows me to use a 'fancy' pointer along the lines of boost::interprocess::offset_ptr with STL types.
As a self contained template the pointer itself works well but I am having trouble getting it to work with either std::vector or boost::containers::vector
The list of things I have implemented for the pointer are:
template<class T>
class OffsetPtr ...
constructors from T*, T&
comparisons <=, <, !=, ==, >=, >=
dereferencing operator* & operator->
assignment
pointer arithmetic ++, --, -, +, +=, -=
explicit operator bool () const;
bool operator! () const;
using iterator_category = std::random_access_iterator_tag;
conversion from OffsetPtr(T) -> OffsetPtr(const T)
rebind conversions from OffsetPtr(T) to OffsetPtr(U)
move semantics - though I think this type should actually be immovable.
pointer traits
random access iterator requirements
iterator traits
conversion between raw pointers and my fancy pointer
comparisions and conversions with nullptr and nullptr_t
The allocator implements
allocator traits
But something, possible several somethings, are still missing.
Do I need template specialisations for OffsetPtr<void> and OffsetPtr<const void> ?
No error messages have suggested this so far but I am aware that rebind() is required
so that we can have void* based implementations of STL containers.
Also:
Do I really need to implement move semantics?
I have always acted as though these are optional for all types.
This is related to my other question
My other question asks how do I verify that I have actually implemented traits for a concept (pre c++20) which is in theory a general question.
See also Implementing a custom allocator with fancy pointers
I have two specific issues which I am so far unable to track down.
One is related to move_iterator and the other either rebind and/or use of void.
Here is an example error when trying to use std::vector:
from /foo/bar/OffsetPtrAllocatorTest.cpp:8:
/usr/include/c++/8/bits/stl_uninitialized.h: In instantiation of _ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = std::move_iter
ator<Memory::OffsetPtr<int, long int> >; _ForwardIterator = Memory::OffsetPtr<int, long int>; _Allocator = Memory::OffsetPtrAllocator<int>]:
/usr/include/c++/8/bits/stl_vector.h:1401:35: required from std::vector<_Tp, _Alloc>::pointer std::vector<_Tp, _Alloc>::_M_allocate_and_copy(std::vector<_Tp, _Alloc>::size_type, _ForwardIterator, _ForwardIter
ator) [with _ForwardIterator = std::move_iterator<Memory::OffsetPtr<int, long int> >; _Tp = int; _Alloc = Memory::OffsetPtrAllocator<int>; std::vector<_Tp, _Alloc>::pointer = Memory::OffsetPtr<int, long int>; st
d::vector<_Tp, _Alloc>::size_type = long unsigned int]
/usr/include/c++/8/bits/vector.tcc:74:12: required from void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp, _Alloc>::size_type) [with _Tp = int; _Alloc = Memory::OffsetPtrAllocator<int>; std::vector<_Tp,
_Alloc>::size_type = long unsigned int]
/foo/bar/OffsetPtrAllocatorTest.cpp:46:16: required from here
/usr/include/c++/8/bits/stl_uninitialized.h:275:25: error: no matching function for call to __gnu_cxx::__alloc_traits<Memory::OffsetPtrAllocator<int>, int>::construct(Memory::OffsetPtrAllocator<int>&, int*, std
::move_iterator<Memory::OffsetPtr<int, long int> >::reference)
__traits::construct(__alloc, std::__addressof(*__cur), *__first);
~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/8/bits/stl_construct.h:61,
from /usr/include/c++/8/deque:62,
from /usr/include/cppunit/Message.h:11,
from /usr/include/cppunit/Exception.h:5,
from /usr/include/cppunit/TestCaller.h:4,
from /usr/include/cppunit/extensions/HelperMacros.h:9,
from /foo/bar/OffsetPtrAllocatorTest.cpp:8:
/usr/include/c++/8/ext/alloc_traits.h:82:7: note: candidate: template<class _Ptr, class ... _Args> static typename std::enable_if<std::__and_<std::is_same<typename std::allocator_traits<_Alloc>::pointer, _Ptr>,
std::__not_<std::is_pointer<_Ptr> > >::value>::type __gnu_cxx::__alloc_traits<_Alloc, <template-parameter-1-2> >::construct(_Alloc&, _Ptr, _Args&& ...) [with _Ptr = _Ptr; _Args = {_Args ...}; _Alloc = Memory::OffsetPtrAllocator<int>; <template-parameter-1-2> = int]
construct(_Alloc& __a, _Ptr __p, _Args&&... __args)
^~~~~~~~~
/usr/include/c++/8/ext/alloc_traits.h:82:7: note: template argument deduction/substitution failed:
I get a different error when trying to use boost::container::vector:
The reason for trying boost as well is that some STL implementations have bugs meaning they don't work as they are supposed to without additional modifications.
I was hoping to expose different flaws in my implementation to help understand what is wrong.
Just returned to this and I now realise that my OffsetPtr class does work with an allocator on gcc 4.8 on RHEL8 but not on gcc 9.4.0 on Ubuntu so the errors are caused by some difference between the two versions.
Returning to this again. I created a complete self contained example which fails to compile on gcc 9.4.0 (Ubuntu 20). This one also fails on 4.8 (RHEL8) though I feel I have got closer than previously by adding construct() templates to the allocator.
The error I get for this case is:
In file included from /usr/include/c++/8/bits/stl_algobase.h:67,
from /usr/include/c++/8/vector:60,
from /home/brucea/scrap/offsetptr2/main.cpp:2:
/usr/include/c++/8/bits/stl_iterator.h: In instantiation of ‘std::move_iterator<_Iterator>::reference std::move_iterator<_Iterator>::operator*() const [with _Iterator = OffsetPtr<int, long int>; std::move_iterator<_Iterator>::reference = int&&]’:
/usr/include/c++/8/bits/stl_uninitialized.h:275:61: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = std::move_iterator<OffsetPtr<int, long int> >; _ForwardIterator = OffsetPtr<int, long int>; _Allocator = OffsetPtrAllocator<int>]’
/usr/include/c++/8/bits/stl_vector.h:1401:35: required from ‘std::vector<_Tp, _Alloc>::pointer std::vector<_Tp, _Alloc>::_M_allocate_and_copy(std::vector<_Tp, _Alloc>::size_type, _ForwardIterator, _ForwardIterator) [with _ForwardIterator = std::move_iterator<OffsetPtr<int, long int> >; _Tp = int; _Alloc = OffsetPtrAllocator<int>; std::vector<_Tp, _Alloc>::pointer = OffsetPtr<int, long int>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’
/usr/include/c++/8/bits/vector.tcc:74:12: required from ‘void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp, _Alloc>::size_type) [with _Tp = int; _Alloc = OffsetPtrAllocator<int>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’
/home/brucea/scrap/offsetptr2/main.cpp:743:16: required from here
/usr/include/c++/8/bits/stl_iterator.h:1047:16: error: invalid static_cast from type ‘const int’ to type ‘std::move_iterator<OffsetPtr<int, long int> >::reference’ {aka ‘int&&’}
{ return static_cast<reference>(*_M_current); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/8/bits/ptr_traits.h: In instantiation of ‘constexpr typename std::pointer_traits<_Ptr>::element_type* std::__to_address(const _Ptr&) [with _Ptr = OffsetPtr<int, long int>; typename std::pointer_traits<_Ptr>::element_type = int]’:
/usr/include/c++/8/ext/alloc_traits.h:84:46: required from ‘static typename std::enable_if<std::__and_<std::is_same<typename std::allocator_traits<_Alloc>::pointer, _Ptr>, std::__not_<std::is_pointer<_Ptr> > >::value>::type __gnu_cxx::__alloc_traits<_Alloc, <template-parameter-1-2> >::construct(_Alloc&, _Ptr, _Args&& ...) [with _Ptr = OffsetPtr<int, long int>; _Args = {int}; _Alloc = OffsetPtrAllocator<int>; <template-parameter-1-2> = int; typename std::enable_if<std::__and_<std::is_same<typename std::allocator_traits<_Alloc>::pointer, _Ptr>, std::__not_<std::is_pointer<_Ptr> > >::value>::type = void]’
/usr/include/c++/8/bits/vector.tcc:103:30: required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int}; _Tp = int; _Alloc = OffsetPtrAllocator<int>]’
/usr/include/c++/8/bits/stl_vector.h:1091:9: required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = int; _Alloc = OffsetPtrAllocator<int>; std::vector<_Tp, _Alloc>::value_type = int]’
/home/brucea/scrap/offsetptr2/main.cpp:744:18: required from here
/usr/include/c++/8/bits/ptr_traits.h:165:31: error: invalid conversion from ‘const int*’ to ‘std::pointer_traits<OffsetPtr<int, long int> >::element_type*’ {aka ‘int*’} [-fpermissive]
{ return std::__to_address(__ptr.operator->()); }
~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
Do I need template [specializations] for OffsetPtr<void> and OffsetPtr<const void>
§16.4.4.6.2 Allocator Completeness Requirements states:
If X is an allocator class for type T, X additionally meets the
allocator completeness requirements if, whether or not T is a complete
type:
(1.1)
X is a complete type, and
(1.2)
all the member types of allocator_­traits<X> other than value_­type are complete types.
Yes, you need a specialization (it's required if OffsetPtrAllocator<void> denoted X X::pointer is OffsetPtr<void>, since, as established it needs to be a complete type [through allocator_traits<X>::pointer]) because void& (reference to 'void') is invalid since void is an incomplete type that cannot be completed (cf: §6.8.2.14 Fundamental types).
Do I really need to implement move semantics?
As far as I understand: your allocator needs a move constructor to be standard compliant. (cf: §16.4.4.6 Cpp17Allocator requirements)
let T, U: cv-unqualified object type.
let X: Allocator<T>
let Y: Allocator<U>
let a: lvalue of type X
let b: lvalue of type Y
X u(std::move(a));
X u = std::move(a);
// Postconditions: The value of a is unchanged and is equal to u.
// Throws: Nothing.
X u(std::move(b));
// Postconditions: u is equal to the prior value of X(b).
// Throws: Nothing.
For the template instantiation errors: in template <class T> struct OffsetPtr the member functions returning T& would be invalid with T = void for the aforementioned reason.
Hope this helps.
I made some changes to pass the compiler, but it still fails at the run time caused by some internal logic error, I believe. And how to make it run correctly is beyond the scope of this answer.
Online Demo
It seems you're mixing up const T* and T * const.
The former refers to a pointer to an object of type const T, and you can't change the object's value through the pointer, but you can change the pointer itself, like pointing to other objects. The latter is vice versa.
So what would you need here?
Suppose you have a const OffsetPtr<int>, it should behave just the same as a pointer of type int* const. And since it's unnecessary to add a const qualifier when returning by value, when you try to get the raw pointer, the function should be like
T* get() const;
It's similar to T& and const T&.
I recommend you to use free functions but not member functions to overload operators. Because of the requirements of LegacyRandomAccessIterator (Or Cpp17RandomAccessIterator for C++20 and later) include the support for n + a, which a of type OffsetPtr is the second parameter, but things look good here.
Ok, back to your origin questions.
Do I need template specialisations for OffsetPtr<void> and OffsetPtr<const void>?
Yes, it's necessary for fulfilling the requirement of an Allocator.(void_pointer and const_void_pointer). And as comments say, under very rare circumstances, the compilation will fail.).
Do I really need to implement move semantics? I have always acted as though these are optional for all types.
Nope. There are no corresponding requirements for Allocator.
The move constructor and move assignment operator is auto-generated for OffsetPtrAllocator by the compiler here. You don't have to worry about it, actually.
static_assert(std::is_move_constructible_v<OffsetPtrAllocator<int>>);
static_assert(std::is_move_assignable_v<OffsetPtrAllocator<int>>);
If you disable the move constructor, you can't directly move a container, but you can move the elements inside.
template <typename T>
class OffsetPtrAllocator
{
OffsetPtrAllocator(OffsetPtrAllocator&&) = delete;
OffsetPtrAllocator& operator=(OffsetPtrAllocator&&) = delete;
}
std::vector<int,OffsetPtrAllocator<int> > v;
// std::vector<int,OffsetPtrAllocator<int> > v1 = std::move(v); // fail
std::vector<int,OffsetPtrAllocator<int> > v2(
std::make_move_iterator(v.begin()),
std::make_move_iterator(v.end())
);
As to GCC 4.8, it still fails. I believe it's an internal "bug", (not very sure), since it's assuming the std::allocator_traits<TrivialAllocator<T>>::pointer can be implicitly converted from int. Source code here.
You can bypass this limitation by declaring a non-explicit constructor with a raw pointer parameter.
Final Demo, compiled under both GCC 4.8 and 9.4.
Since the mentioned bug still exists in GCC, maybe this code only works for std::vector.

Vector of structs with const members

I'm coming back to C++ after many years and I'm on the C++17 standard. As per this question, it seems custom structs with const members are not always compatible with vectors without public copy-assignment constructors.
In my case though, unlike the linked question, the following compiles fine:
#include <vector>
struct Foo { const int m_Bar; };
int main()
{
std::vector<Foo> vecFoos{ Foo{1} };
vecFoos.push_back({ Foo{2} });
return 0;
}
And it's the following that doesn't:
#include <vector>
struct Foo { const int m_Bar; };
int main()
{
std::vector<Foo> vecFoos{ Foo{1} };
vecFoos.assign({ Foo{2} }); // using assign instead of push_back
return 0;
}
It fails with:
$ g++ --std=c++17 main.cpp && ./a.out
In file included from /usr/include/c++/9/vector:60,
from main.cpp:1:
/usr/include/c++/9/bits/stl_algobase.h: In instantiation of ‘static _Tp* std::__copy_move<_IsMove, true, std::random_access_iterator_tag>::__copy_m(const _Tp*, const _Tp*, _Tp*) [with _Tp = Foo; bool _IsMove = false]’:
/usr/include/c++/9/bits/stl_algobase.h:404:30: required from ‘_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false; _II = const Foo*; _OI = Foo*]’
/usr/include/c++/9/bits/stl_algobase.h:441:30: required from ‘_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false; _II = const Foo*; _OI = Foo*]’
/usr/include/c++/9/bits/stl_algobase.h:474:7: required from ‘_OI std::copy(_II, _II, _OI) [with _II = const Foo*; _OI = Foo*]’
/usr/include/c++/9/bits/vector.tcc:321:29: required from ‘void std::vector<_Tp, _Alloc>::_M_assign_aux(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const Foo*; _Tp = Foo; _Alloc = std::allocator<Foo>]’
/usr/include/c++/9/bits/stl_vector.h:793:2: required from ‘void std::vector<_Tp, _Alloc>::assign(std::initializer_list<_Tp>) [with _Tp = Foo; _Alloc = std::allocator<Foo>]’
main.cpp:8:30: required from here
/usr/include/c++/9/bits/stl_algobase.h:382:39: error: static assertion failed: type is not assignable
382 | static_assert( __assignable::type::value, "type is not assignable" );
In a less trivial real-world example where I'm trying to reassign some structs with const members to a vector, I run into this issue, although the compiler complains with different messages:
error: cannot bind rvalue reference of type ‘std::optional<long unsigned int>&&’ to lvalue of type ‘const std::optional<long unsigned int>’
and in the same output quite a few of:
error: non-static const member ‘const uint64_t myStruct::m_MyMember’, can’t use default assignment operator
error: no matching function for call to ‘std::optional<long unsigned int>::operator=(const std::optional<long unsigned int>&) const’
Are all those three related? I just want to understand why I can reassign new elements to an existing vector and if it's solely due to my structs not having an explicit copy assignment operator. I suspect the answer is yes since adding my own copy-assignment constructor does bypass the issue in the sample code:
Foo& operator=(Foo other) { return *this; }
But this seems silly and I just want to find a sensible way to reassign these to a vector.
The problem with const members is that they disable compiler-generated operator=. So, if you use the vector in a manner that operator= is not needed, or if you provide your own operator= somehow, it all works fine. Generally, you never strictly need const member fields, and they cause a lot of problems with STL containers. It's better to avoid them.
But this seems silly and I just want to find a sensible way to reassign these to a vector.
You can't assign to a const int anywhere, why would a data member be special? That's what const means.
You are able to construct Foos, which allows you to push_back, as that only requires CopyInsertable, not any kind of assignable.
assign requires CopyAssignable, which is not strictly necessary, but is a hint that implementations should re-use existing elements.

copy constructor doesn't work when the class is used as a map value

I can't find a simpler way to explain my problem than straight pasting it here(a simplified version though).
I have a templated class with necessary assignment operator, default constructor and a common copy constructor. when I try to use this class in my code I get an error as follows:
#include<map>
#include<vector>
template<class T>
class BufferContainer
{
public:
BufferContainer& operator=(BufferContainer& other)
{
buffer = other.get();
return *this;
}
BufferContainer( const BufferContainer& other ) :
buffer( other.get() )
{
}
BufferContainer(){
}
std::vector<T>& get() {
return buffer;
}
void add(T value) {
buffer.push_back(value);
}
std::vector<T> buffer;
};
int main()
{
std::map<int, BufferContainer<int> > myMap;
myMap[1].add(1);
return 1;
}
and the error is:
Practice $ g++ template.cpp
template.cpp: In instantiation of ‘BufferContainer<T>::BufferContainer(const BufferContainer<T>&) [with T = int; BufferContainer<T> = BufferContainer<int>]’:
/usr/include/c++/4.7/bits/stl_pair.h:105:31: required from ‘std::pair<_T1, _T2>::pair(const _T1&, const _T2&) [with _T1 = const int; _T2 = BufferContainer<int>]’
/usr/include/c++/4.7/bits/stl_map.h:458:11: required from ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = int; _Tp = BufferContainer<int>; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, BufferContainer<int> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = BufferContainer<int>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]’
template.cpp:38:9: required from here
template.cpp:16:26: error: passing ‘const BufferContainer<int>’ as ‘this’ argument of ‘std::vector<T>& BufferContainer<T>::get() [with T = int]’ discards qualifiers [-fpermissive]
I will appreciate if you help me with the methods to solve this issue and, more importantly, tell me why i got this error.
thanks
Your get() function is not const-qualified, and you are invoking it through a reference to const in your copy-constructor:
BufferContainer( const BufferContainer& other ) :
// ^^^^^
buffer( other.get() )
// ^^^^^^^^^^^
{
}
This is why the compiler is complaining. You cannot call a non-const function through a reference to const. Having a reference to const means you're not going to modify the state of the referenced object, so you can only invoke functions that promise not to modify the object's state. That's what the const qualifier on member functions is for - to make that promise.
Therefore, your member function get() should be qualified as const, and return a reference to a const vector:
std::vector<T> const& get() const
// ^^^^^ ^^^^^
{
return buffer;
}
If you need your non-const function get() because you want to allow clients to modify the internal buffer (advice: consider whether this is really a good idea), then you will have to provide two overloads of get():
A const-qualified one that returns a reference to a const vector (like the one shown above)
A non-const-qualified one that returns a reference to a modifiable vector (like the original get())
What it means is that you should provide a const version of your get() method:
const std::vector<T>& get() const
{
return buffer;
}
since the copy constructor rightly takes a const reference:
BufferContainer( const BufferContainer& other )
This means you can only call const methods on other.
Note that the return type is of the const version of get() is a const reference. This is required for const correctness.
other is taken as const reference and get is a non-const method. You can't call a non-const method from a const reference.

How can I insert a structure as key into a map?

I'm getting a compiling error for the code below, after removing the comment characters from the first insert line. I'm unable to insert the structure into the map while inserting integers is fine.
# include <iostream>
# include <map>
using namespace std;
struct node
{int test;} temp;
int main()
{
temp.test = 24;
int test = 30;
map<node, bool> mymap1;
map<int, bool> mymap2;
//mymap1.insert(make_pair(temp, true));
mymap2.insert(make_pair(test, true));
return 0;
}
How can I fix the error?
For a type to serve as the key for a map, it has to be ordered. All that means, practically, is that operator< must be defined for the type. If you defined a global operator<(const node&, const node&), this should work fine; i.e.,
bool operator<(const node& n1, const node& n2) {
return n1.test < n2.test;
}
A std::map's keys are stored internally in a binary search tree. In order for keys to be stored and searched for in a binary search tree, they must be comparable. For example, a requirement of a binary search tree is that a left child's key is less than its parent's key and a right child's key is greater than its parent's key. However, if the keys are not comparable, how are we supposed to tell whether the children are greater or less than the parent? We cannot form a tree and therefore std::map will not work with these types.
You simply need to define the less than operator like so:
bool operator<(const node& n1, const node& n2)
{
return n1.test < n2.test;
}
This also must be a friend of your node struct if the "test" data member is private (It's public now since node is currently a struct). However, I would probably do it like this:
#include <map>
class node
{
public:
int getTest() const { return _test; }
void setTest(int test) { _test = test; }
private:
int _test;
};
bool operator<(const node& n1, const node& n2)
{
return n1.getTest() < n2.getTest();
}
int main()
{
std::map<node,bool> foo;
node n;
n.setTest(25);
foo[n] = true;
return 0;
}
Here's how to read the error messages:
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’:
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_tree.h:1141: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = node, _Val = std::pair<const node, bool>, _KeyOfValue = std::_Select1st<std::pair<const node, bool> >, _Compare = std::less<node>, _Alloc = std::allocator<std::pair<const node, bool> >]’
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_map.h:469: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const std::pair<const _Key, _Tp>&) [with _Key = node, _Tp = bool, _Compare = std::less<node>, _Alloc = std::allocator<std::pair<const node, bool> >]’
prog.cpp:15: instantiated from here
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
First, we ignore most of the 'instantiated from' lines, because they're just talking about how the templates got expanded. The important one is the last one, referring to our source code, because it tells us where the error was triggered. Of course, we knew that anyway, so we'll skip that too. We'll also ignore the path to the library header in question, because we don't really care how the compiler stores its stuff.
stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’:
stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
So... our code indirectly calls ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = node]’, or if we actually do that substitution, ‘bool std::less<node>::operator()(const node&, const node&) const’. And this is a problem because there is no match for ‘operator<’ in ‘__x < __y’.
__x and __y are variables inside the std::less implementation (you should be able to guess that much). From the name, we can guess (and if we had studied the standard library, we would know) that std::less is a template function that compares two things of the same type and returns whether or not the first is less than the second.
How does it do that? By using the operator<, of course. So that's what we need to do to fix the problem: it says the operator< isn't there for what's being compared, so we have to provide it. What's being compared? nodes, of course. So we define operator< for our class.
Why does it do that? So that we can write functions that accept a comparison-operation as an argument (either a template argument or a run-time parameter - but the former is far more common), and pass std::less. That's the reason for std::less's existence: it turns the act of comparing things into a function, and actual functions are somewhat more useful.
How is that relevant? Because, like the others said, std::map actually is passing std::less as an argument. It's actually a default argument to the std::map template that's used to compare elements. After all, part of the interface of a map is that every key is unique. How are you going to check keys for uniqueness if you can't compare them? Granted, technically you would only have to compare them for equality for that to work. But it turns out that being able to order the keys makes it possible to create a much more efficient data structure. (You would know about this if you actually took courses in university about programming and CS.)
Why wasn't there a problem with int? You should be able to guess by now: operator< already naturally works for ints. But you have to tell C++ how to do it for any user types, because you might have something else in mind.
C++11
As mentioned in Andrew Rasmussen's answer, the keys of a std::map must be comparable. However, you can also provide a custom comparison object to your map instead of defining operator< for your struct. Moreover, since C++11, you can use a lambda expression instead of defining a comparison object. As a result, you can keep your code as short as follows:
auto comp = [](const node& n1, const node& n2) { return n1.test < n2.test; };
std::map<node, bool, decltype(comp)> mymap1(comp);
Code on Ideone

How do I add a copy constructor and assignment operator to this class?

I'm having trouble adding a copy constructor to this class:
http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html
I need to add it so that I can add the concurrent_queue in an stl vector container.
Tried the below, and it almost compiles. In fact, if I remove the_mutex and the_condition_variable from the copy constructor it compiles. When I add it back in it doesn't. The assignment operator seems to be OK with regards to compilation.
concurrent_queue(concurrent_queue<Data> const& rhs):
the_queue(rhs.the_queue),
the_mutex(rhs.the_mutex),
the_condition_variable(rhs.the_condition_variable)
{
}
concurrent_queue<Data>& operator = (concurrent_queue<Data> const& rhs)
{
if (this == &rhs) return *this; // check for self assignment
the_queue = rhs.the_queue;
the_mutex(rhs.the_mutex);
the_condition_variable(rhs.the_condition_variable);
}
The error I get is the following:
concurrentqueue.h: In copy constructor ‘concurrent_queue<Data>::concurrent_queue(const concurrent_queue<Data>&) [with Data = Packet*]’:
/usr/include/c++/4.4/bits/stl_construct.h:74: instantiated from ‘void std::_Construct(_T1*, const _T2&) [with _T1 = concurrent_queue<Packet*>, _T2 = concurrent_queue<Packet*>]’
/usr/include/c++/4.4/bits/stl_uninitialized.h:187: instantiated from ‘static void std::__uninitialized_fill_n<<anonymous> >::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = concurrent_queue<Packet*>*, _Size = long unsigned int, _Tp = concurrent_queue<Packet*>, bool <anonymous> = false]’
/usr/include/c++/4.4/bits/stl_uninitialized.h:223: instantiated from ‘void std::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = concurrent_queue<Packet*>*, _Size = long unsigned int, _Tp = concurrent_queue<Packet*>]’
/usr/include/c++/4.4/bits/stl_uninitialized.h:318: instantiated from ‘void std::__uninitialized_fill_n_a(_ForwardIterator, _Size, const _Tp&, std::allocator<_Tp2>&) [with _ForwardIterator = concurrent_queue<Packet*>*, _Size = long unsigned int, _Tp = concurrent_queue<Packet*>, _Tp2 = concurrent_queue<Packet*>]’
/usr/include/c++/4.4/bits/stl_vector.h:1035: instantiated from ‘void std::vector<_Tp, _Alloc>::_M_fill_initialize(size_t, const _Tp&) [with _Tp = concurrent_queue<Packet*>, _Alloc = std::allocator<concurrent_queue<Packet*> >]’
/usr/include/c++/4.4/bits/stl_vector.h:230: instantiated from ‘std::vector<_Tp, _Alloc>::vector(size_t, const _Tp&, const _Alloc&) [with _Tp = concurrent_queue<Packet*>, _Alloc = std::allocator<concurrent_queue<Packet*> >]’
test.cpp:18: instantiated from here
concurrentqueue.h:24: error: no match for call to ‘(boost::mutex) (boost::mutex&)’
EDIT:
It seems that boost mutex inherits non-copyable and condition variable I think is the same.
The interesting thing is that in the assignment I'm able to copy it. Why would that even compile? Do I need to worry about the fact that it's non-copyable in practice for the situation I'm using it for?
Both boost::mutex and boost::condition_variable only have default constructors. Aside from that, you don't want their (potentially locked) state to be copied - just use the default constructors.
Also you shouldn't copy the_queue directly as this is not thread-safe.
AFAIK, boost::mutex and boost::condition are not copyable (and there is no meaning to copying a mutex lock anyways). Construct them using the default constructor in your copy constructor as follows:
concurrent_queue(concurrent_queue<Data> const& rhs):
the_queue(rhs.the_queue),
the_mutex(),
the_condition_variable()
{
}
Note that there are problems with making the copy constructor thread safe. If you're not copying objects between threads, that shouldn't be a problem, though. Following the idea in your previous question, where all concurrent_queueobjects are pre-allocated, this shouldn't be a problem.