Рассмотрим следующий пример:
class MyWrapper {
public:
MyWrapper() {};
private:
ThirdPartyLibraryType impl;
};
Предположим, что ThirdPartyLibraryType
не имеет конструктора по умолчанию, и я ничего не могу поделать, так как это сторонний тип библиотеки. Тогда этот код не будет компилироваться, так как конструктор по умолчанию MyWrapper()
должен вызвать конструктор по умолчанию ThirdPartyLibraryType
, Теперь я просто хочу, чтобы конструктор по умолчанию ничего не делал, потому что мой вариант использования конструктора по умолчанию следующий:
std::array<MyWrapper,10> myArray;
for (int i=0;i<10;++i) {
myArray[i] = generateMyWrapper(...);
}
Есть ли способ форсировать создание конструктора по умолчанию? (Без накладных расходов я не хочу использовать очевидное решение ThirdPartyLibraryType*
имеет член данных)
Редактировать:
До сих пор я использовал подход, похожий на предложенный Yakk, который не использует конструктор по умолчанию, но я думаю, что случай вызова функции-члена выглядит ужасно:
template<class T, size_t N, class C, class CF>
std::array<T,N> gen_array(C const& obj, CF&& f) {
...
use obj.f
...
}
class MyClass {
MyWrapper generateMyWrapper(int i) const { ... }
auto genMyWrapperTypeArray() const {
return
gen_array<
MyWrapper,10,MyClass,
MyWrapper(MyClass::*)(int) const
> (
*this, &MyClass::generateMyWrapper
);
}
}
Я не проверял его, суть в том, что если синтаксис не точно такой же, он все равно будет намного сложнее, чем простой цикл for, который я использовал бы, если бы был доступен конструктор по умолчанию.
std::experimental::optional
или эквивалент повышения имеет небольшие издержки, но имеет дело с «это может быть или не построено».
Небезопасный эквивалент будет std::aligned_storage_t
где ты занимаешься строительством. Проблема в разрушении, вам нужно знать, построено ли оно.
Лучшим вариантом может быть необязательный весь массив, и вообще не иметь нулевого аргумента ctor. Упакуйте то, что вам нужно для создания каждого аргумента, и создайте конструктор, принимающий это вместо цикла.
template<class T, size_t...Is, class F>
std::array<T,sizeof...(Is)> gen_array(
std::index_sequence<Is...>, F&&f
){
return {{f(Is)...}};
)template<class T, size_t N, class F>
std::array<T,N> gen_array(
F&&f
){
return gen_array(std::make_index_sequence<N>{}, f);
)
Выше вызывается переданная лямбда с индексом для каждого элемента для создания элемента.
Если ThirdPartyLibraryType
не имеет конструктора по умолчанию, есть вероятность, что это связано с действительным проектное решение / причина.
Тогда у вас есть два подхода:
либо вы найдете подходящие параметры для конструктора не по умолчанию, и вы используете их в своей оболочке;
или вы задерживаете строительство вашего ThirdPartyLibraryType
До тех пор, пока вы не знаете, какой из его конструкторов использовать и с какими параметрами.
Первый подход будет выглядеть так:
class MyWrapper {
public:
MyWrapper() : impl(/*parameters that you've selected*/) {};
private:
ThirdPartyLibraryType impl;
};
Perhap отвечает вашим потребностям, потому что вы уже знаете, какие параметры конструктора вы хотите использовать, или потому что вы можете позволить себе создание недорогого объекта и перезаписать его позже. Но это не всегда хорошая идея …
Второй подход будет использовать указатель, который вы хотели бы избежать. Этот подход имеет много преимуществ:
Фактически, очевидный подход с указателем заставит вашу оболочку реализовать PIMPL идиома.