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.
Related
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.
I have a function that returns an std::vector of a class that contains a std::unique_ptr as member. I need to store this vector object on the heap so I can pass it through a C-style DLL interface.
See the following code sample:
#include <iostream>
#include <cstdlib>
#include <memory>
#include <vector>
// I have control over the following two classes
class SomeBigClassWithManyMembers { };
class MyClass
{
std::unique_ptr<SomeBigClassWithManyMembers> up;
public:
static const std::vector<MyClass> GetMany()
{
// imagine this does lots of work
return std::vector<MyClass>(50);
}
// following code is suggested in https://stackoverflow.com/questions/31430533/move-assignable-class-containing-vectorunique-ptrt but doesn't help
/*
MyClass() { }
MyClass(MyClass&& other): up{std::move(other.up)} { }
MyClass& operator=(MyClass&& other)
{
up = std::move(other.up);
return *this;
}
*/
};
// Imagine that I can't change this C-style code - it's a fixed interface provided by someone else
struct NastyCStyleStruct
{
void* v;
};
void NastyCStyleInterface(NastyCStyleStruct s) { printf("%u", (unsigned int)((std::vector<MyClass>*)s.v)->size()); }
int main()
{
NastyCStyleStruct s;
s.v = new std::vector<MyClass>(std::move(MyClass::GetMany()));
NastyCStyleInterface(s);
return 0;
}
Note that in my actual code, the vector needs to outlive the function in which it is created (because this is being done in a DLL), so writing
auto vec = MyClass::GetMany();
s.v = &vec;
would not suffice. The vector must be stored on the heap.
The trouble here is that the code seems to try to use the (non-existent) copy constructor of MyClass. I can't understand why the copy constructor is being invoked, because I am explicitly asking for move semantics with std::move. Not even initialising s.v to a fresh new std::vector of the appropriate size and calling the three-argument version of std::move works.
Errors from g++:
In file included from /usr/include/c++/7/memory:64:0,
from stackq.cpp:4:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = MyClass; _Args = {const MyClass&}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*; bool _TrivialValueTypes = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:134:15: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*]’
/usr/include/c++/7/bits/stl_uninitialized.h:289:37: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*; _Tp = MyClass]’
/usr/include/c++/7/bits/stl_vector.h:331:31: required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = MyClass; _Alloc = std::allocator<MyClass>]’
stackq.cpp:43:65: required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: use of deleted function ‘MyClass::MyClass(const MyClass&)’
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
stackq.cpp:10:7: note: ‘MyClass::MyClass(const MyClass&)’ is implicitly deleted because the default definition would be ill-formed:
class MyClass
^~~~~~~
stackq.cpp:10:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = SomeBigClassWithManyMembers; _Dp = std::default_delete<SomeBigClassWithManyMembers>]’
In file included from /usr/include/c++/7/memory:80:0,
from stackq.cpp:4:
/usr/include/c++/7/bits/unique_ptr.h:388:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
How can I repair this code so that the vector object itself is stored on the heap?
The problem here is that GetMany is defined to return a const std::vector. Even though it's returning by value, so there's no risk of unwanted side-effects if you mutate the result, the compiler still enforces the type (auto just copied the exact return type of the function after all), making it impossible to move from it. So instead of cheaply copying a few pointer/size_t-sized values (to the vector contents, size and capacity), it had to do a full copy construction, including copy constructing all the values stored in the original vector. That fails due to the unique_ptr member in the stored instances.
Simply dropping the const from the return type should allow std::move/vector's move constructor to do their job, allowing the ultra-cheap extraction of the contents of the vector returned by GetMany with no copies (or even moves) of any instance of MyClass.
I'm trying to reserve some spots into an std::vector, and having an error I don't understand:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Foo{
public:
std::string str;
int i;
Foo(){
this->str = "";
this->i = 0;
}
Foo(Foo ©Foo){
this->str = copyFoo.str; //Or get methods if private members
this->i = copyFoo.i;
}
Foo(std::string str, int i){
this->str = str;
this->i = i;
}
};
int main()
{
std::vector<Foo> fooVector;
fooVector.reserve(20); // Error
for(int i=0; i<20; i++){
fooVector[i] = Foo("Test", i); // Or should I use operator new?
// Or should I even stick to the push_back method?
}
return 0;
}
Of course I could just not reserve, and it would probably work. But now I'm interested in why it is not working now. I added the copy constructor because it looked that could be the problem I was having back then. But after adding the copy constructor, it doesn't work either.
The error says:
In file included from
/usr/local/gcc-4.8.1/include/c++/4.8.1/vector:62:0,
from main.cpp:3: /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_construct.h: In
instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 =
Foo; _Args = {Foo}]':
/usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_uninitialized.h:75:53:
required from 'static _ForwardIterator
std::__uninitialized_copy<TrivialValueTypes>::_uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator; _ForwardIterator = Foo*; bool
_TrivialValueTypes = false]' /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_uninitialized.h:117:41:
required from '_ForwardIterator
std::uninitialized_copy(_InputIterator, _InputIterator,
_ForwardIterator) [with _InputIterator = std::move_iterator; _ForwardIterator = Foo*]' /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_uninitialized.h:258:63:
required from '_ForwardIterator
std::__uninitialized_copy_a(_InputIterator, _InputIterator,
_ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator; _ForwardIterator = Foo*; _Tp = Foo]'
/usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_vector.h:1142:29:
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; _Tp = Foo; _Alloc = std::allocator;
std::vector<_Tp, _Alloc>::pointer = Foo*; std::vector<_Tp,
_Alloc>::size_type = long unsigned int]' /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/vector.tcc:75:70:
required from 'void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp,
_Alloc>::size_type) [with _Tp = Foo; _Alloc = std::allocator; std::vector<_Tp, _Alloc>::size_type = long unsigned int]'
main.cpp:31:24: required from here
/usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_construct.h:75:7:
error: no matching function for call to 'Foo::Foo(Foo)'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^ /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_construct.h:75:7:
note: candidates are: main.cpp:22:5: note: Foo::Foo(std::string, int)
Foo(std::string str, int i){
^ main.cpp:22:5: note: candidate expects 2 arguments, 1 provided main.cpp:17:5: note: Foo::Foo(Foo&)
Foo(Foo ©Foo){
^ main.cpp:17:5: note: no known conversion for argument 1 from 'Foo' to 'Foo&' main.cpp:12:5: note: Foo::Foo()
Foo(){
^ main.cpp:12:5: note: candidate expects 0 arguments, 1 provided
Where is the problem? Do I need to initialize the std:vector objects, or just assign each position to an object instance?
EDIT: I'm using C++11. And If I remove the copy constructor, I receive the following error at the reserve method line:
required from 'void std::_Construct(_T1*, _Args&& ---) [with _TI = Foo; _Args = {Foo&}]
That's why I wrote the copy constructor in the first place.
I don't want to use the resize method because I want the size method to return the actual number of Foo objects contained into the vector, not the amount I reserved.
std::vector<Foo> fooVector;
fooVector.reserve(20); // Error
for(int i=0; i<20; i++){
fooVector[i] = Foo("Test", i); // Or should I use operator new?
// Or should I even stick to the push_back method?
}
The code above is wrong. You are accessing elements beyond size() in the container. The idiomatic way of doing that would be doing push_back/emplace_back on the container to actually create the objects, rather than only memory:
for(int i=0; i<20; i++){
fooVector.emplace_back("Test", i); // Alternatively 'push_back'
}
Other than that, in C++11 the type used in the container requires either a copy or move constructor.
Change
Foo(Foo ©Foo) // bah!!! This can't make copies from temporaries
to
Foo(const Foo ©Foo) // now that's one good-looking copy constructor
The first problem is that your copy constructor takes its argument by non-const reference, which prevents copying from temporaries.
In this case, there's no need to declare a copy constructor at all: if you don't, then one will be implicitly defined to copy each member, which is what you want.
If the class were sufficiently complicated to need a non-default copy constructor, then it should be
Foo(Foo const &);
Note that you don't necessarily need a copy constructor to store a type in a vector; a move constructor is sufficient if you don't want your class to be copyable.
The second problem is that you're accessing uninitialised elements of the vector. reserve just reserves memory for that many elements without initialising them; you need to insert elements (with resize, insert, push_back, emplace_back or whatever) before you can access them.
As noted by Luchian Grigore in his answer, first you should fix your copy constructor. Or just remove it, because the default one will do just fine here. Secondly, to use the code as you have it now you should use resize instead of reserve:
std::vector<Foo> fooVector;
fooVector.resize(20);
The latter only reserves memory, but container's size remains the same - 0 in your case so it's illegal to access elements. resize on the other hand does what you intended.
But in general you shouldn't even do that (as noted here for example). Just use push_back or emplace_back on an empty vector to fill it with contents. resize will call the default constructor for each element you intend to overwrite anyway.
I have some compilation problems pushing back elements of type T to a vector when compiling with g++ -std=c++0x.
This is a minimal example:
#include <vector>
using namespace std;
class A {
public:
A() { }
A& operator=(A &orig) {
return *this;
}
};
int main(int argc, char **argv) {
A a;
vector<A> b;
A c = a; // This is fine
b.push_back(a); // This is not, but only when compiling with -std=c++0x!
return 0;
}
It compiles fine with g++ -Wall -pedantic, but it gives this error when compiling with g++ -Wall -pedantic -std=c++0x:
In file included from /usr/include/c++/4.4/vector:69,
from min.cpp:1:
/usr/include/c++/4.4/bits/vector.tcc: In member function ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, _Args&& ...) [with _Args = const A&, _Tp = A, _Alloc = std::allocator<A>]’:
/usr/include/c++/4.4/bits/stl_vector.h:741: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = A, _Alloc = std::allocator<A>]’
min.cpp:20: instantiated from here
/usr/include/c++/4.4/bits/vector.tcc:314: error: no match for ‘operator=’ in ‘__position.__gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = A*, _Container = std::vector<A, std::allocator<A> >]() = ((const A&)((const A*)std::forward [with _Tp = const A&](((const A&)((const A*)__args#0)))))’
min.cpp:11: note: candidates are: A& A::operator=(A&)
In file included from /usr/include/c++/4.4/vector:61,
from min.cpp:1:
/usr/include/c++/4.4/bits/stl_algobase.h: In static member function ‘static _BI2 std::__copy_move_backward<true, false, std::random_access_iterator_tag>::__copy_move_b(_BI1, _BI1, _BI2) [with _BI1 = A*, _BI2 = A*]’:
/usr/include/c++/4.4/bits/stl_algobase.h:595: instantiated from ‘_BI2 std::__copy_move_backward_a(_BI1, _BI1, _BI2) [with bool _IsMove = true, _BI1 = A*, _BI2 = A*]’
/usr/include/c++/4.4/bits/stl_algobase.h:605: instantiated from ‘_BI2 std::__copy_move_backward_a2(_BI1, _BI1, _BI2) [with bool _IsMove = true, _BI1 = A*, _BI2 = A*]’
/usr/include/c++/4.4/bits/stl_algobase.h:676: instantiated from ‘_BI2 std::move_backward(_BI1, _BI1, _BI2) [with _BI1 = A*, _BI2 = A*]’
/usr/include/c++/4.4/bits/vector.tcc:308: instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, _Args&& ...) [with _Args = const A&, _Tp = A, _Alloc = std::allocator<A>]’
/usr/include/c++/4.4/bits/stl_vector.h:741: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = A, _Alloc = std::allocator<A>]’
min.cpp:20: instantiated from here
/usr/include/c++/4.4/bits/stl_algobase.h:561: error: no match for ‘operator=’ in ‘* -- __result = std::move [with _Tp = A&](((A&)(-- __last)))’
min.cpp:11: note: candidates are: A& A::operator=(A&)
So it seems that it doesn't find the right operator= of A. Why? Why it states with _Iterator = A* when I'm passing A?
The Assignable requirement imposted by the language standard on the standard container elements requires the t = u expression to be valid even if u is a const object. The requirement was defined that way since C++98 (see 23.1/4)
You violated that requirement, since your assignment operator does not accept const objects. This immediately mean that your class A cannot be used as a container element type.
Why it worked in C++03 is rather irrelevant. It worked by accident. It is obvious from the error message that the C++0x implementation of the library uses some C++0x specific features (like std::move), which is what makes the above requirement to come into play. But anyway, a C++03 implementation (and even C++98 implementation) can also fail to compile for your A.
Your example with A c = a; is irrelevant, since it does not use the assignment operator at all (why is it here?).
In order to fix the error you should either accept the parameter by const reference or by value.
I'm quite sure this is a safety feature. Types with a copy-assignment operator (or a copy constructor) that may mutate the right-hand side are not safe to use in standard containers - an example of this is (the now deprecated) std::auto_ptr which will break horribly if stored in a container.
The old C++03 library implementation permitted such unsafe code, but apparently they implemented a compile-time check in the C++0x version -- probably in conjunction with move-enabling the containers.
The standard's definition of copy-assignment operator is (section [class.copy]):
A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&.
But the X& and volatile X& variants may not be compatible with containers assuming an assignment can be made from an r-value RHS.
NOTE: Passing by value, e.g. X::operator=(X) is a fundamental part of the copy-and-swap idiom.
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.