stl — определение значения NULL / Empty для стандартных типов C ++

В моем классе есть методы, которые возвращают const <Container>& потому что я не хочу, чтобы возвращаемый контейнер был изменен снаружи, и копирование может быть дорогим. например: const std::set<ClassA>& foo() и я хочу foo() чтобы иметь возможность вернуть константную ссылку на пустую std::set<ClassA> если это должно. то есть

const std::set<ClassA>& foo(const std::string& key) {
std::map<std::string, std::set<ClassA>>::iterator itr = m_elements.find(key);
return (itr != m_elements.end()) ? *itr : /*empty std::set<ClassA>*/;
}

Но я не могу вернуть константную ссылку на временно созданную пустую std::set<ClassA> в foo(), Чтобы решить эту проблему, я определяю общий шаблонный синглтон-класс в общем месте, чтобы его можно было использовать с любым типом

template <typename T> class cNull
{
public:
static const T& Value() {
static cNull<T> instance;
return instance.d;
}
private:
cNull() {};
cNull(const cNull<T>& src);
void operator=(cNull<T> const&);
T d;
};

А сейчас foo() может быть что-то вроде

const std::set<ClassA>& foo(const std::string& key) {
std::map<std::string, std::set<ClassA>>::iterator itr = m_elements.find(key);
return (itr != m_elements.end()) ? *itr : cNull<std::set<ClassA> >.Value();
}

Мне было интересно, есть ли лучший способ решить эту проблему и есть ли какие-либо проблемы с этим дизайном?

0

Решение

Мне было интересно, есть ли лучший способ решить эту проблему и есть ли какие-либо проблемы с этим дизайном?

Конечно, есть: не возвращайте ссылку, а копию. Когда вы говорите, что «вы хотите иметь возможность вернуть пустую std::set<T>«Вы просто заявляете, что можете изменить возвращаемое значение относительно его исходного состояния (в качестве переменной-члена). В этом случае копия вполне подойдет.

1

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

В такой ситуации вы обычно две альтернативы:

  • Либо вы выбрасываете исключение, если элемент не найден.
  • Или вернуть какой-нибудь объект, например boost::optional который может быть содержать найденный элемент.
1

Немного зависит от того, почему вы возвращаете ссылку.

Если это потому, что вызывающая сторона ожидает сохранить ссылку и отразить в ней изменения, сделанные через объект, для которого foo что произойдет, если вы вернете ссылку на ваш пустой набор синглтона, и тот же ключ будет позже добавлен в m_elements? Ссылка не делает то, что рекламируется. Может быть, лучше добавить пустой набор на карту, когда foo называется, поэтому код становится:

const std::set<int>& foo(const std::string& key) {
return m_elements[key];
}

Если вы возвращаете ссылку исключительно по соображениям производительности (чтобы избежать потенциально дорогой копии большого набора), а не потому, что вам действительно нужна семантика ссылки на часть объекта, то возврат статического пустого набора будет работать, и я полагаю, нет ничего плохого в том, чтобы иметь помощника шаблона для его достижения, если вы обнаружите, что часто делаете одно и то же с несколькими типами. Будьте очень осторожны, чтобы документально подтвердить, что ссылка вернулась может или не может отражать дальнейшие изменения объекта (в зависимости от того, key присутствовал когда foo был вызван, хотя вы не могли бы гарантировать, что). Тогда вызывающие абоненты будут знать, чтобы не использовать его после даты его продажи, то есть всякий раз, когда кто-либо в следующий раз вносит соответствующее изменение в объект.

Если вы не знаете, почему вы возвращаете ссылку, либо верните копию, либо выясните, что ожидают вызывающие абоненты. делать с этим набором, и заменить foo с одной или несколькими функциями, которые это делают. Таким образом, вы не позволяете ссылкам на внутренности вашего класса попадать в руки пользователей.

Я предположил, что пустой набор верен по какой-то веской причине — возможно, чтобы избежать того, чтобы каждый вызывающий объект проверял возвращаемое значение и / или проверял существование key перед звонком foo,

1

Есть несколько вариантов сделать это, но прежде чем вы сможете выбрать один, вы должны ответить: мне действительно нужно вернуть ссылку?

Если вы пытаетесь воздействовать на результат (и хотите, чтобы он был отражен в изменениях — и не можете изменить интерфейс), ответ — да. Если какое-либо из этих условий изменится, вы можете вернуться копированием, что значительно облегчит это:

std::set<int> foo(const std::string& key) const
{
std::set<int> results;
std::map<std::string, std::set<int>>::iterator it = m_elements.find(key); // assuming m_elements is std::map<std::string, std::set<int>>
if (it != m_elements.end())
{
results = it->second;
}
return results;
}

Если вы должны вернуть ссылку, вы можете сделать что-то вроде boost::optional (или использовать std::pair для имитации), бросить исключение, иметь внутренний нуль-набор. Опция, наиболее близкая к тому, что вы пытаетесь сделать, — это необязательный / парный подход:

std::pair<bool, std::set<int>*> foo(const std::string& key)
{
std::pair<bool, std::set<int>*> results = std::make_pair(false, nullptr);
std::map<std::string, std::set<int>>::iterator it = m_elements.find(key);
if (it != m_elements.end())
{
results.first = true;
results.second = &(it->second);
}
return results;
}

Затем вы можете проверить логическое значение (гарантированно действительное), чтобы увидеть, является ли значение указателя допустимым. boost::optional делает что-то подобное (и если вы можете использовать boost, предпочитайте это, используя std::pair версия для симуляции).

0

Я бы вернул указатель или nullptr для обозначения не найдено.

const std::set<ClassA>* foo(const std::string& key)
{
auto it = m_elements.find(key);
if (it == m_elements.end()) return NULL;
return &(*it);
}

просто, и он не страдает от синглтонита.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector