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.
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'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.
Below code compiles fine with clang but not gcc , any explanation is this a bug in gcc?
Its just a class which contains a vector of unique_ptr and std::function as member and when I create vector of this class , I cant say reserve or resize on this. push_back works fine with std::move, while this only happens with gcc and not clang.
#include <algorithm>
#include <memory>
#include <utility>
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
class ABC
{
public:
ABC()
{}
private:
std::vector<std::unique_ptr<int>> up;
std::function<void (int*)> func;
};
int main()
{
ABC a;
std::vector<ABC> vec;
vec.reserve(1);
}
Error message looks like below for gcc
In file included from /opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_tempbuf.h:60:0,
from /opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_algo.h:62,
from /opt/wandbox/gcc-7.1.0/include/c++/7.1.0/algorithm:62,
from prog.cc:1:
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::unique_ptr<int>; _Args = {const std::unique_ptr<int, std::default_delete<int> >&}]':
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/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 std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*; bool _TrivialValueTypes = false]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:134:15: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/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 std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*; _Tp = std::unique_ptr<int>]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_vector.h:331:31: required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >]'
prog.cc:10:7: required from 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = ABC; _Args = {const ABC&}]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:83:18: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const ABC*; _ForwardIterator = ABC*; bool _TrivialValueTypes = false]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:134:15: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const ABC*; _ForwardIterator = ABC*]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:289:37: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const ABC*; _ForwardIterator = ABC*; _Tp = ABC]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_vector.h:1263: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 = const ABC*; _Tp = ABC; _Alloc = std::allocator<ABC>; std::vector<_Tp, _Alloc>::pointer = ABC*; std::vector<_Tp, _Alloc>::size_type = long unsigned int]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/vector.tcc:73:40: required from 'void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp, _Alloc>::size_type) [with _Tp = ABC; _Alloc = std::allocator<ABC>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]'
prog.cc:24:18: required from here
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_construct.h:75:7: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/wandbox/gcc-7.1.0/include/c++/7.1.0/memory:80:0,
from prog.cc:2:
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/unique_ptr.h:388:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
This happens because std::function's move ctor is not noexcept, but a std::vector can only use the move ctor if it's noexcept (strong exception guarantee).
The issue is that std::unique_ptr is (obviously) non-copyable, so that makes ABC noncopyable as a whole.
To make ABC noexcept-movable implicitly, it'd need every one of its members to be noexcept-movable as well.
If you remove the std::function, that's what happens: the .resize() doesn't need to copy A.up (a std::vector's move operator is noexcept), so the std::unique_ptrs (inside up) can just be moved, and everything works fine.
See this coliru: if you comment the noexcept, vec.reserve() will need to copy everything, and the issue comes back.
Adding ABC(ABC&&) = default fixes the issue because user-declared (though defaulted) move ctor inhibits the generation of copy constructor (see here).
We can also go the opposite way and manually delete A's copy constructor in the coliru (and keep the move ctor not noexcept): here.
To fix the issue, you could store the std::function in a std::unique_ptr, which has a nothrow move ctor. See here
I think that GCC is being picky about constructors; it blocks the generation of the move-ctor1. It stops complaining after you provide them explicitely:
class ABC
{
public:
ABC() = default; // <--
ABC(ABC&&) = default; // <--
private:
std::vector<std::unique_ptr<int>> up;
std::function<void (int*)> func;
};
1 I think (again, I'm not sure), that his happens because the default copy-ctor of ABC is defined as noexcept and is as such preferred over the default move-ctor (it's still ill-formed because we're dealing with non-copyable members here). Trying to generate a defaulted noexcept move-ctor result in:
main.cpp:14:4: note: 'ABC::ABC(ABC&&) noexcept' is implicitly deleted because its exception-specification does not match the implicit exception-specification ''
ABC(ABC&&) noexcept = default;
^~~
As such, forcing the generation of no-noexcept move-ctor allows it to be picked. Why Clang doesn't have a problem with that - I don't know.
One explanation for the above is hinted by the fact that removing std::function allows the defaulted noexcept move-ctor to be generated. std::function doesn't have a noexcept move-ctor (which sucks tremendously), so the whole class falls back to the copy-ctor. Since unique_ptr doesn't have one, the whole thing breaks.
GCC accepted the lack of noexcept on std::function's move ctor as bug
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81017
// -- C++ --
/** 2227 * #brief %Function move constructor. 2228 * #param
__x A %function object rvalue with identical call signature. 2229 * 2230
* The newly-created %function contains the target of #a __x 2231 * (if
it has one). 2232 */ 2233
function(function&& __x) : _Function_base() { __x.swap(*this); }
I have simplified my code as follows.
#include <vector>
class NoncopyableItem {
public:
NoncopyableItem() { }
NoncopyableItem(NoncopyableItem &&nt) { };
};
class Iterator {
friend class Factory;
public:
~Iterator() { } // weird
private:
Iterator() { }
std::vector<NoncopyableItem> buffer_;
};
class Factory {
public:
Iterator NewIterator() {
return Iterator();
}
};
int main() {
Factory fa;
auto it = fa.NewIterator();
return 0;
}
I want to leverage the RVO (return value optimization) in function NewIterator, but I got the following error:
In file included from /usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/vector:62:0,
from /cygdrive/c/Users/DELL/ClionProjects/destructor_test/main.cpp:1:
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = NoncopyableItem; _Args = {const NoncopyableItem&}]':
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_uninitialized.h:75:53: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const NoncopyableItem*, std::vector<NoncopyableItem> >; _ForwardIterator = NoncopyableItem*; bool _TrivialValueTypes = false]'
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_uninitialized.h:126:41: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const NoncopyableItem*, std::vector<NoncopyableItem> >; _ForwardIterator = NoncopyableItem*]'
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_uninitialized.h:279:63: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const NoncopyableItem*, std::vector<NoncopyableItem> >; _ForwardIterator = NoncopyableItem*; _Tp = NoncopyableItem]'
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_vector.h:324:32: required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = NoncopyableItem; _Alloc = std::allocator<NoncopyableItem>]'
/cygdrive/c/Users/DELL/ClionProjects/destructor_test/main.cpp:7:7: required from here
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_construct.h:75:7: error: use of deleted function 'constexpr NoncopyableItem::NoncopyableItem(const NoncopyableItem&)'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^
/cygdrive/c/Users/DELL/ClionProjects/destructor_test/main.cpp:2:7: note: 'constexpr NoncopyableItem::NoncopyableItem(const NoncopyableItem&)' is implicitly declared as deleted because 'NoncopyableItem' declares a move constructor or move assignment operator
class NoncopyableItem {
^
CMakeFiles/destructor_test.dir/build.make:62: recipe for target 'CMakeFiles/destructor_test.dir/main.cpp.o' failed
According to cppreference.com, NewIterator() should meet the requirement of RVO. However, it seems that the compiler trys to call the default copy constructor of Iterator, then fails because Iterator.buffer_ is noncopyable.
Well, to my suprise, if I delete the destructor of Iterator in L#13, the code works fine.
Why does the destructor affect the RVO behavior of the compiler?
First of all, forget about RVO in this context. It is a legal optimization, but even when it does happen, the code must be legal without it.
So with this in mind, we look at
auto it = fa.NewIterator();
This line attempts to construct a new Iterator from a temporary Iterator. For this to work, we need either of the following two†:
Iterator(const Iterator&); //or
Iterator(Iterator&&);
Now in the code you posted, trying to use the implicitly declared Iterator(const Iterator&); will result in the compiler error you showed because the copy constructor of the non-static member buffer_ fails to compile.
The second candidate is not generated because Iterator has a user defined destructor.
If you remove the user defined destructor, the compiler will generate the move constructor Iterator(Iterator&&); and use it as we are constructing from a temporary. Or maybe it won't and do RVO instead, but it could use it, and that is the important part.
†Or some other user declared constructor that makes the line legal of course. But above are the two commonly compiler generated ones you apparently ask about.
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.