std :: vector класса с частным конструктором не компилируется при использовании современных С++

Код, над которым я работаю, изначально был разработан с использованием C++03 и компилируется и функционирует без ошибок с использованием g++ -std=c++03. Моя цель состоит в том, чтобы скомпилировать тот же код с помощью g++ -std=c++17.

Код содержит MyClass который содержит NestedClass. Только MyClass должен иметь возможность использовать, создавать и изменять экземпляры NestedClass, которые хранятся в std::vector< NestedClass >. Таким образом, NestedClass содержит частный конструктор и объявляет MyClass и std::vector< NestedClass > качестве друзей.

Минимальный пример:

#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;
}

Этот минимальный пример не выполняется при компиляции -std=c++17 со следующей ошибкой:

/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){ }

Как я могу переписать код так, чтобы он компилировался с использованием С++ 17?

Изменение std::vector< NestedClass > на std::vector< NestedClass * > не является вариантом, поскольку для этого потребуется переписать код, который использует MyClass который я не контролирую.

Ответ 1

Минимальное исправление для вашего конкретного примера

data.resize( 40, {} );

Вызовите частного конструктора самостоятельно, так что vector нужно только вызвать (неявно объявленные) публичные.


В общем, подружиться с чем-то в библиотеке, которую вы не контролируете, не работает. Вы понятия не имеете, сказал ли кто-нибудь что-то на деле, чтобы делегировать работу на что-то еще.

В vector случае в значительной степени требуется делегировать указанную работу чему-то еще.


Правильное исправление, вероятно, будет связано с изменениями в соответствующих классах. Одна из возможностей - идиома passkey: сделать конструкторы общедоступными, но только вызываемыми с аргументом частного типа.