The code I'm working on was initially designed using C++03
, and compiles and functions without errors using g++ -std=c++03
. My goal is to have the same code compile using g++ -std=c++17
.
The code contains a MyClass
which contains a NestedClass
. Only MyClass
should be able to use, create, and modify instances of NestedClass
, which are stored in a std::vector< NestedClass >
. As such NestedClass
contains a private constructor, and declares MyClass
and std::vector< NestedClass >
as friends.
Minimal Example:
#include <vector>
class MyClass {
public:
class NestedClass {
friend class MyClass;
friend class std::vector< NestedClass >;
double _d;
NestedClass( double d = 0.0 ) : _d(d){ }
};
private:
std::vector< NestedClass > data;
public:
MyClass(){
data.resize( 40 );
}
};
int main(){
MyClass myclass = MyClass();
return 0;
}
This minimal example fails when compiling -std=c++17
with the following error:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = MyClass::NestedClass; _Args = {}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:527:18: required from ‘static _ForwardIterator std::__uninitialized_default_n_1<_TrivialValueType>::__uninit_default_n(_ForwardIterator, _Size) [with _ForwardIterator = MyClass::NestedClass*; _Size = long unsigned int; bool _TrivialValueType = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:583:20: required from ‘_ForwardIterator std::__uninitialized_default_n(_ForwardIterator, _Size) [with _ForwardIterator = MyClass::NestedClass*; _Size = long unsigned int]’
/usr/include/c++/7/bits/stl_uninitialized.h:645:44: required from ‘_ForwardIterator std::__uninitialized_default_n_a(_ForwardIterator, _Size, std::allocator<_Tp>&) [with _ForwardIterator = MyClass::NestedClass*; _Size = long unsigned int; _Tp = MyClass::NestedClass]’
/usr/include/c++/7/bits/vector.tcc:563:35: required from ‘void std::vector<_Tp, _Alloc>::_M_default_append(std::vector<_Tp, _Alloc>::size_type) [with _Tp = MyClass::NestedClass; _Alloc = std::allocator<MyClass::NestedClass>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’
/usr/include/c++/7/bits/stl_vector.h:692:21: required from ‘void std::vector<_Tp, _Alloc>::resize(std::vector<_Tp, _Alloc>::size_type) [with _Tp = MyClass::NestedClass; _Alloc = std::allocator<MyClass::NestedClass>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’
bug.cpp:21:20: required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: ‘MyClass::NestedClass::NestedClass(double)’ is private within this context
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bug.cpp:11:4: note: declared private here
NestedClass( double d = 0.0 ) : _d(d){ }
How can I rewrite the code so that it compiles using c++17?
Changing std::vector< NestedClass >
to std::vector< NestedClass * >
is not an option since it would require rewriting code that uses MyClass
which I do not control.
The minimalistic fix for your particular example is
data.resize( 40, {} );
Call the private constructor yourself, so that vector
only needs to call the (implicitly declared) public ones.
In general, befriending something in a library you don't control doesn't work. You have no idea whether said something is actually going to delegate the work to something else.
In vector
's case, it is pretty much required to delegate said work to something else.
A proper fix will likely involve changes to the classes involved. One possibility is the passkey idiom: make the constructors public, but only callable with an argument of a private type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With