Пока @lubgr ответил на вопрос, на который я спросил, я думаю, что стоит отметить другое решение, которое у меня есть для моей конкретной проблемы.Вопрос возник из-за создания класса для инкапсуляции ленивой инициализации членов.Моя первоначальная попытка была
template <typename T>
class Lazy {
mutable boost::once_flag _once;
mutable boost::optional<T> _data;
std::function<T()> _factory;
void Init() const { boost::call_once([&] { _data = _factory(); }, _once); }
public:
explicit Lazy(std::function<T()> factory):_once(BOOST_ONCE_INIT),_factory(factory){}
T& Value() {
Init();
return *_data;
}
};
, которая может быть использована как
class Foo {
int _a;
Lazy<int> _val;
Foo(a):_a(a):_val([this](){return this->_a+1;}){}
}
Foo f(10);
int val = f._val.Value();
, но имеет ту же проблему, которую я задал в своем вопросе, в том, что this
является круговой ссылкой, которая не 'сохраняются для копирования конструкции.Решение не в том, чтобы создать собственный конструктор копирования и, возможно, в перемещении конструктора, а в том, чтобы исправить класс реализации Lazy, чтобы мы могли передать arg фабрике.
Новая реализация Lazy для членов:
template <typename T, typename TThis>
class LazyMember {
mutable boost::once_flag _once;
mutable boost::optional<T> _data;
typedef std::function<T(TThis const*)> FactoryFn;
FactoryFn _factory;
void Init(TThis const * arg0) const { boost::call_once([&] { _data = _factory(arg0); }, _once); }
public:
explicit LazyMember(FactoryFn factory):_once(BOOST_ONCE_INIT),_factory(factory){}
T& Value(TThis const * arg0) { Init(arg0); return *_data; }
T const & Value(TThis const * arg0) const { Init(arg0); return *_data; }
};
, который используется как
class Foo {
int _a;
Lazy<int> _val;
Foo(a):_a(a):_val([](Foo const * _this){return _this->_a+1;}){}
}
Foo f(10);
int val = f._val.Value(&f);
, и у него нет проблем с циклической ссылкой, и, следовательно, не требуется пользовательский конструктор копирования / перемещения.