У меня есть следующий код:
MyType x = do_something_dangerous();
// ...
if (some_condition) {
// ...
bar(x);
}
else {
// ...
}
// ...
if (another_condition_which_may_depend_on_previous_if_else} {
// ...
baz(x);
}
Идея состоит в том, что в некоторых случаях, которые, возможно, трудно / неудобно определить заранее, мне нужно использовать x
, Но в тех случаях, когда мне не нужно его использовать, попытка его инициализации может быть плохой (скажем, может привести к сбою моего процесса).
Теперь, кажется, что мне нужно использовать это инициализация по требованию владельца (ссылка фокусируется на Java, так что вот набросок): Wrapper<MyType>
или же Wrapper<MyType, do_something_dangerous>
с get()
метод, такой, что первый get()
звонки do_something_dangerous()
и позже get()
Просто передайте значение первого полученного вызова.
Заметки:
boost::optional
, но это было бы немного громоздко, а также искажать предполагаемое использование: «Рекомендуется использовать optional<T>
в ситуациях, когда существует ровно одна, понятная (для всех сторон) причина отсутствия значения типа T
и где недостаток стоимости столь же естественен, как наличие какой-либо регулярной стоимости T
«.ИМХО, предлагаемое вами решение идеально подходит:
Конечно, если вы уже используете boost в своем приложении, вы можете посмотреть на предложенный модуль boost :: опциональный, но я не уверен, что это именно то, что вам нужно, так как он больше предназначен для объектов, допускающих обнуляемость, когда то, что вам нужно, игнорируется. инициализация.
Мой совет здесь: придерживайтесь специальной реализации оболочки.
В отличие от других ответов, я думаю, вы должны не использовать синглтон, но просто что-то вроде (реализация с использованием параметра инициализации):
template<typename T, typename I>
class LazyInit {
T* val;
I init;
public:
LazyInit<T, I>(I init): init(init) {
val = NULL;
}
~LazyInit<T, I>() {
delete val;
val = NULL; // normally useless here
}
T& get() {
if (val == NULL) {
val = new T(init);
}
return *val;
}
};
А вот реализация, использующая функцию инициализации:
template<typename T>
class LazyInit {
T* val;
T (*init)();
public:
LazyInit<T>(T (*init)()): init(init) {
val = NULL;
}
~LazyInit<T>() {
delete val;
val = NULL; // normally useless here
}
T& get() {
if (val == NULL) {
val = new T(init());
}
return *val;
}
};
...
LazyInit<MyType> x(do_something);
...
bar(x.get()); // initialization done only here
Вы можете легко объединить оба, чтобы построить реализацию, используя функцию, принимающую параметр.
Если у вас есть C ++ 11, вы можете использовать комбинацию std::async
а также std::shared_future
:
#include <iostream>
#include <future>
using namespace std;
struct LazyObj {
LazyObj() {
cout << "LazyObj init" << endl;
}
void test() {
cout << "LazyObj test" << endl;
}
};
int main() {
auto x = std::async(launch::deferred, []() -> LazyObj& {
static LazyObj a;
return a;
}).share();
int cond=0;
if( cond == 0 ) {
x.get().test();
x.get().test();
}
return 0;
}
В этом случае использования launch::deferred
точки std::async
не создавать поток выполнения и вызывать лямбду по требованию, когда get()
называется. Чтобы разрешить несколько звонков get()
метод, мы конвертируем std::future
в std::shared_future
с помощью share()
метод. Теперь мы можем получить объект lazy-initialisaion, когда или где это необходимо.