Класс посетителя с большим общим состоянием: лучший способ реализации ссылочной семантики?

Этот вопрос свободно основан на библиотеке Boost.Graph (BGL), которая использует посетитель-как шаблон для настройки рекурсивных (поисковых) алгоритмов. BGL передает объекты посетителя по значению (по аналогии с объектами функции STL) и документация состояния

Так как параметр посетителя передается по значению, если ваш посетитель содержит состояние, то любые изменения состояния в течение алгоритма будут внесены в копию объекта посетителя, а не переданного объекта посетителя. Поэтому вы можете захотеть, чтобы посетитель удерживал это состояние указателем или ссылкой.

Мой вопрос: каков наилучший способ реализации ссылочной семантики классов посетителей с отслеживанием состояния? Абстрагируясь от классов точных указателей (необработанные или уникальные против общих, константные или неконстантные), что было бы лучшим местом для размещения ссылки: в передаче параметров или в элементе данных?

Альтернатива 1: посетитель хранит состояние указателем и передается по значению (как в Boost.Graph)

class Visitor
{
public:
Visitor(): state_(new State()) {}
void start() { /* bla */ }
void finish() { /* mwa */ }
private:
State* state_;
}

template<typename Node, typename Visitor>
int algorithm(Node const& n, Visitor v)
{
v.start();
algorithm(next(n), v);
v.finish();
}

Альтернатива 2: посетитель содержит данные по значению и передается по указателю

class Visitor
{
public:
Visitor(): state_() {}
void start() { /* bla */ }
void finish() { /* mwa */ }
private:
State state_;
}

template<typename Node, typename Visitor>
int algorithm(Node const& n, Visitor* v)
{
v->start();
algorithm(next(n), v);
v->finish();
}

Моя текущая склонность: Я нахожу Альтернативу 1 [передачу по значению объектов, которые содержат указатели / ссылки], немного неудобной, потому что посетитель не удовлетворяет семантике значений, поэтому я бы предпочел сделать семантику ссылок понятной в списке параметров [Альтернатива 2]. Есть ли какие-либо другие соображения или альтернативы, которые актуальны здесь?

4

Решение

Я понимаю ваш дискомфорт по поводу Альтернативы 1, но я думаю, что это тот случай, когда «этот автобус ушел»; Другими словами, направление стандартной библиотеки C ++ (и Boost, а не только BGL) благоприятствует использованию шаблона «хранимой ссылки».

Рассмотрим, например, повсеместное использование функторов, которые можно реализовать с помощью лямбда-выражений. Насколько я знаю, все интерфейсы стандартной библиотеки (и boost) передают аргументы функтора по значению, поэтому, если функтор хранит состояние, он должен хранить его по ссылке. Следовательно, мы должны привыкнуть к [&](){} скорее, чем [=](){}, И, по аналогии, мы должны привыкнуть к тому, что посетители держат ссылки (или указатели, но я предпочитаю ссылки) на их состояние.

На самом деле есть веская причина передавать функторы (и посетителей) по значению, а не по ссылке. Если они были переданы по ссылке, они должны быть переданы либо const&что сделало бы невозможным изменение состояния или &, что сделало бы невозможным использование временных значений. Единственной другой возможностью будет передача явного указателя, но его нельзя использовать с лямбдами или временными значениями (если временные значения не были излишне выделены в куче).

1

Другие решения

Есть третий вариант:

class Visitor
{
public:
Visitor(): state_() {}
void start() { /* bla */ }
void finish() { /* mwa */ }
private:
State state_;
};

template<typename Node, typename Visitor>
int algorithm(Node const& n, Visitor v)
{
v.start();
algorithm(next(n), v);
v.finish();
}

// Set the reference semantics here, use value everywhere else
algorithm(myNode, boost::ref(myVisitor)); // ... or std::ref in c++11

Я думаю, что это обычно одобряется стандартом, в отличие от явной маркировки чего-либо как указателя или ссылки. В конце концов, std::ref а также std::cref были введены для решения этой проблемы.

С другой стороны, в книге «Стандарты кодирования C ++» Саттер и Александреску утверждают, что функторы всегда должны легко и быстро копироваться. Они рекомендуют использовать внутренний блок с подсчетом ссылок (IIRC, здесь нет книги). Так что пока std::ref или же std::cref решит вашу проблему, они чаще используются для «адаптации» не функторных объектов, например при прохождении std::vector через std::bind,

Альтернатива 1, с shared_ptr<T> (или еще лучше: shared_ptr<T const>), вероятно, ваш лучший вариант. В любом случае вы просто «оборачиваете» семантику указателя за семантикой значения для кода BGL, что нормально, если вы правильно настроили время жизни объекта.

2

Бессмысленно пытаться сделать вашего посетителя без гражданства, когда на самом деле у него есть состояние. Я не вижу никаких проблем с (2).

0
По вопросам рекламы [email protected]