Nobilis
Nobilis

Reputation: 7448

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.

Upvotes: 1

Views: 575

Answers (2)

Caleth
Caleth

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 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.

Upvotes: 1

Aykhan Hagverdili
Aykhan Hagverdili

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

Related Questions