Reputation: 7448
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.
Upvotes: 1
Views: 575
Reputation: 63039
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 Foo
s, 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.
Upvotes: 1
Reputation: 29985
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.
Upvotes: 4