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

0

В моем классе есть методы, возвращающие const <Container>& потому, что я не хочу, чтобы возвращаемый контейнер менялся снаружи, и копирование могло быть дорогостоящим. для примера: const std::set<ClassA>& foo() и я хочу, чтобы foo() мог возвращать ссылку const для пустого 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>*/;
}

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

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();
}

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

  • 1
    Почему вы должны вернуть ссылку?
  • 0
    Почему бы просто не иметь переменную уровня класса m_emptySet и возвращать ссылку на нее? (Он может быть изменен вызывающей стороной так же, как наборы данных в m_elements, но это не было частью вопроса)
Показать ещё 6 комментариев
Теги:
stl

5 ответов

1

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

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

  • 0
    Может быть, мое утверждение немного сбивает с толку, я хочу вернуть константную ссылку на пустой набор - это то, что я имел в виду.
  • 0
    @blueskin, зачем тебе возвращать ссылку?
1

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

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

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

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

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

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

1

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

  • Либо вы производите исключение, если ни один элемент не найден.
  • Или верните некоторый объект, например boost::optional который может содержать найденный элемент.
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

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

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

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
    "Мне действительно нужно вернуть ссылку?" : Да. потому что копирование std :: set может быть дорогим и объекты в наборе должны быть копируемыми объектами.
  • 0
    «Может быть дорого» - это зависит от того, насколько большой ваш сет. Набор из 100 целых чисел не будет дорогим для копирования ... набор из 1 000 000 целых может быть. Самое простое решение вашей проблемы, если вы настаиваете на том, чтобы не копировать набор, - это вернуть экземпляр std::pair<bool, set<int>*> .
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню