Consider the following C++ code:
#include <cstdint>
#include <vector>
template<typename BaseType>
class InitInt {
public:
using base_type = BaseType;
private:
BaseType value_;
public:
InitInt() : value_(0) {}
explicit InitInt(BaseType value) : value_(value) {}
InitInt(
const InitInt<BaseType>& other)
: value_(other.get()) {}
const BaseType& get() const { return value_; }
};
using InitInt16 = InitInt<int16_t>;
class IntInt16PairVector {
public:
std::vector<std::pair<InitInt16, InitInt16>> m;
};
void f(IntInt16PairVector& v) {
v.m = {{InitInt16(0), InitInt16(0)}};
}
int main(void) {
IntInt16PairVector v;
f(v);
}
When compiling this code using GCC 12.3.0 with flags -O3
and -Wall
, it warns about use of an uninitialized value inside the function f()
.
In copy constructor ‘InitInt<BaseType>::InitInt(const InitInt<BaseType>&) [with BaseType = short int]’,
inlined from ‘std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = InitInt<short int>; _T2 = InitInt<short int>]’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/stl_pair.h:195:17,
inlined from ‘void std::_Construct(_Tp*, _Args&& ...) [with _Tp = pair<InitInt<short int>, InitInt<short int> >; _Args = {const pair<InitInt<short int>, InitInt<short int> >&}]’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/stl_construct.h:119:7,
inlined from ‘_ForwardIterator std::__do_uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const pair<InitInt<short int>, InitInt<short int> >*; _ForwardIterator = pair<InitInt<short int>, InitInt<short int> >*]’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/stl_uninitialized.h:120:21,
inlined from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::pair<InitInt<short int>, InitInt<short int> >*; _ForwardIterator = std::pair<InitInt<short int>, InitInt<short int> >*; bool _TrivialValueTypes = false]’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/stl_uninitialized.h:137:32,
inlined from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const pair<InitInt<short int>, InitInt<short int> >*; _ForwardIterator = pair<InitInt<short int>, InitInt<short int> >*]’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/stl_uninitialized.h:185:15,
inlined from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, allocator<_Tp>&) [with _InputIterator = const pair<InitInt<short int>, InitInt<short int> >*; _ForwardIterator = pair<InitInt<short int>, InitInt<short int> >*; _Tp = pair<InitInt<short int>, InitInt<short int> >]’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/stl_uninitialized.h:372:37,
inlined from ‘void std::vector<_Tp, _Alloc>::_M_assign_aux(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const std::pair<InitInt<short int>, InitInt<short int> >*; _Tp = std::pair<InitInt<short int>, InitInt<short int> >; _Alloc = std::allocator<std::pair<InitInt<short int>, InitInt<short int> > >]’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/vector.tcc:339:35,
inlined from ‘std::vector<_Tp, _Alloc>& std::vector<_Tp, _Alloc>::operator=(std::initializer_list<_Tp>) [with _Tp = std::pair<InitInt<short int>, InitInt<short int> >; _Alloc = std::allocator<std::pair<InitInt<short int>, InitInt<short int> > >]’ at /usr/lib/gcc/x86_64-pc-linux-gnu/12.3.0/include/c++/bits/stl_vector.h:785:21,
inlined from ‘void f(IntInt16PairVector&)’ at test.cpp:32:37:
test.cpp:19:11: warning: ‘<anonymous>’ may be used uninitialized [-Wmaybe-uninitialized]
19 | : value_(other.get()) {}
| ^~~~~~~~~~~~~~~~~~~
test.cpp: In function ‘void f(IntInt16PairVector&)’:
test.cpp:32:44: note: ‘<anonymous>’ declared here
32 | v.m = {{InitInt16(0), InitInt16(0)}};
| ^
I think this warning is due to assigning an initializer list into the vector using uninitialized copy. How can I remove this?
Why GCC decides to warn there is not 100% clear to me, but I suspect that as you provided your own copy constructor, GCC somehow chooses to assume, that you might to some funny business there. It seems that it is a false positive of GCC's warning (note that the warning is maybe-uninitialized and not uninitialized). This seems to be a known issue see here.
To get rid of the warning, remove your custom copy constructor. As you do memberwise copy, it's better to let the compile handle it and provide default constructors for you. Furthermore, by providing a copy constructor but no copy assignment operator you violated rule of three. Consider reading about when the compiler will generate default constructors for you.