Возвращая «пустую ссылку» в C ++?

В динамически типизированных языках, таких как JavaScript или PHP, я часто выполняю такие функции, как:

<code>function getSomething(name) {
    if (content_[name]) return content_[name];
    return null; // doesn't exist
}
</code>

Я возвращаю объект, если он существует илиnull если не.

Что было бы эквивалентно в C ++ с использованием ссылок? Есть ли рекомендуемый шаблон в целом? Я видел некоторые рамки, имеющиеisNull() Метод для этого:

<code>SomeResource SomeClass::getSomething(std::string name) {
    if (content_.find(name) != content_.end()) return content_[name];
    SomeResource output; // Create a "null" resource
    return output;
}
</code>

Затем вызывающая сторона проверяет ресурс таким образом:

<code>SomeResource r = obj.getSomething("something");
if (!r.isNull()) {
    // OK
} else {
    // NOT OK
}
</code>

Однако необходимость реализации такого рода магического метода для каждого класса кажется тяжелой. Также не представляется очевидным, когда внутреннее состояние объекта должно быть установлено из "null". чтобы "не ноль".

Есть ли альтернатива этому шаблону? Я уже знаю, что это можно сделать с помощью указателей, но мне интересно, как / если это можно сделать с помощью ссылок. Или я должен отказаться от возврата "null"? объекты в C ++ и использовать какой-то специфичный для C ++ шаблон? Любое предложение о том, как это сделать, будет оценено.

 jalf29 апр. 2012 г., 12:58
Отредактировал ваш вопрос, чтобы добавить примечание об этом, делая понятным для читателейwhy Вы спрашиваете об этом со ссылками
 hmjd29 апр. 2012 г., 11:34
Имеет ли "помимо использования указателей" исключить умные указатели?
 Martin York29 апр. 2012 г., 11:41
Ну ваши предложенияisNull() это не типичный C ++. Какую реальную проблему вы пытаетесь решить. Может быть, мы сможем предложить лучшие идеи, если поймем, что вы пытаетесь сделать.
 Mat29 апр. 2012 г., 11:39
Возвращение итераторов - это шаблон, который встречается довольно часто. Вы возвращаете неверный итератор, если вещь не была найдена.
 jalf29 апр. 2012 г., 11:41
Почему вы эффективно спрашиваете, как решить эту проблему,beside the correct way of solving it& Quot ;? Почему вы не хотите использовать указатели для указателей вариантов использования, предназначенных для решения?

Ответы на вопрос(7)

Приведенный ниже код демонстрирует, как вернуть «неверный» Рекомендации; это просто другой способ использования указателей (обычный метод).

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

#include <iostream>
#include <cstddef>

#define Nothing(Type) *(Type*)nullptr
//#define Nothing(Type) *(Type*)0

struct A { int i; };
struct B
{
    A a[5];
    B() { for (int i=0;i<5;i++) a[i].i=i+1; }
    A& GetA(int n)
    {
        if ((n>=0)&&(n<5)) return a[n];
        else return Nothing(A);
    }
};

int main()
{
    B b;
    for (int i=3;i<7;i++)
    {
        A &ra=b.GetA(i);
        if (!&ra) std::cout << i << ": ra=nothing\n";
        else std::cout << i << ": ra=" << ra.i << "\n";
    }
    return 0;
}

МакросNothing(Type) возвращаетvalueв этом случае, который представленnullptr - вы можете также использовать0, на который установлен адрес ссылки. Этот адрес теперь можно проверить, как если бы вы использовали указатели.

 18 июн. 2015 г., 01:56
На Xcode 6.3 это никогда не будет работать: если (! & Amp; ra), адрес ссылки всегда будет иметь значение false.
Решение Вопроса

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

With a pointer (note: this requires that the resource doesn't get destructed while the caller has a pointer to it; also make sure the caller knows it doesn't need to delete the object):

SomeResource* SomeClass::getSomething(std::string name) {
    std::map<std::string, SomeResource>::iterator it = content_.find(name);
    if (it != content_.end()) 
        return &(*it);  
    return NULL;  
}

Using std::pair with a bool to indicate if the item is valid or not (note: requires that SomeResource has an appropriate default constructor and is not expensive to construct):

std::pair<SomeResource, bool> SomeClass::getSomething(std::string name) {
    std::map<std::string, SomeResource>::iterator it = content_.find(name);
    if (it != content_.end()) 
        return std::make_pair(*it, true);  
    return std::make_pair(SomeResource(), false);  
}

Using boost::optional:

boost::optional<SomeResource> SomeClass::getSomething(std::string name) {
    std::map<std::string, SomeResource>::iterator it = content_.find(name);
    if (it != content_.end()) 
        return *it;  
    return boost::optional<SomeResource>();  
}

Если вы хотите использовать семантику значений и иметь возможность использовать Boost, я рекомендую третий вариант. Основное преимуществоboost::optional надstd::pair является ли это унифицированнымboost::optional Значение не создает тип, который его инкапсулирует. Это означает, что он работает для типов, которые не имеют конструктора по умолчанию, и экономит время и память для типов с нетривиальным конструктором по умолчанию.

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

 22 февр. 2015 г., 17:05
Хороший ответ. В C ++ 11 std :: unique_ptr & lt; SomeResource & gt; или std :: shared_ptr & lt; SomeResource & gt; могут быть возвращены также. Первый будет соответствовать семантике значений в том смысле, что ресурс должен быть скопирован, а второй будет соответствовать варианту 1 (но, вероятно, требует рефакторинга SomeClass's content_, чтобы также содержать shared_ptrs). Во всяком случае, в обоих случаях управление памятью намного понятнее, чем в варианте 1.
 29 апр. 2012 г., 12:06
+1: конечно, не так уж сложно накатить свой собственный «опционально» класс, если буст зависимость не желательна.
 29 апр. 2012 г., 13:26
@Troubadour: на самом деле на удивление трудно свернуть свой собственный «необязательный». который может содержать любой тип; вам нужно встроить объект POD с правильным размером и выравниванием для типа, что совсем не так просто. (Конечно, это намного проще, если вы ограничите его типами, которые могут быть созданы по умолчанию, и в этом случае он, по сути, будет таким же, как вариант 2).

Один хороший и относительно ненавязчивый подход, который позволяет избежать проблемы при реализации специальных методов для всех типов, - это использование сboost.optional, По сути, это шаблонная оболочка, которая позволяет вам проверить, является ли сохраненное значение «действительным». или нет.

Кстати, я думаю, что это хорошо объяснено в документации, но остерегайтесьboost::optional изboolэто конструкция, которую трудно интерпретировать.

Edit: Вопрос задается по поводу «NULL ссылки», но фрагмент кода имеет функцию, которая возвращает значение. Если эта функция действительно вернула ссылку:

const someResource& getSomething(const std::string& name) const ; // and possibly non-const version

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

const someResource* getSomething(const std::string& name) const; // and possibly non-const version

но ты должен сделать этоabsolutely clear что вызывающая сторона не становится владельцем указателя и не должна пытаться удалить его.

 this.lau_29 апр. 2012 г., 11:45
Мне очень нравится этот подход, я посмотрю на него.
 29 апр. 2012 г., 12:02
@ Я согласен, это, вероятно, проще в контексте названия вопроса. Тело вопроса предполагает, что OP также нуждается в средствах для проверки правильности возвращаемых значений. Кроме того, при возврате указателей нужно быть абсолютно уверенным, что право собственности четко определено.
 29 апр. 2012 г., 12:29
@Benj точно, вопрос в том, как сообщить вызывающей стороне, является ли значение допустимым или нет.
 29 апр. 2012 г., 12:14
Есть ли у boost.optional много преимуществ перед использованием unique_ptr и shared_ptr? Оба они могут быть проверены на nullptr, чтобы определить, являются ли они недействительными.
 29 апр. 2012 г., 11:53
IMO немного сложнее - используйте указатели, как говорит @jalf, нет необходимости в дополнительных библиотеках и в совершенстве c ++. необязательный требует семантики значения, то есть то, что возвращено, является копией (я возвращался по ссылке в прошлом, но не знаю, является ли это!)

Вот пара идей:

Альтернатива 1:

class Nullable
{
private:
    bool m_bIsNull;

protected:
    Nullable(bool bIsNull) : m_bIsNull(bIsNull) {}
    void setNull(bool bIsNull) { m_bIsNull = bIsNull; }

public:
    bool isNull();
};

class SomeResource : public Nullable
{
public:
    SomeResource() : Nullable(true) {}
    SomeResource(...) : Nullable(false) { ... }

    ...
};

Альтернатива 2:

template<class T>
struct Nullable<T>
{
    Nullable(const T& value_) : value(value_), isNull(false) {}
    Nullable() : isNull(true) {}

    T value;
    bool isNull;
};

в отличие от Java и C # в C ++ ссылочный объект не может быть нулевым.
поэтому я бы посоветовал 2 метода, которые я использую в этом случае.

1 - вместо ссылки используйте тип с нулевым значением, такой как std :: shared_ptr

2 - получить ссылку как выходной параметр и вернуть Boolean для успеха.

bool SomeClass::getSomething(std::string name, SomeResource& outParam) {
    if (content_.find(name) != content_.end()) 
    {
        outParam = content_[name];
        return true;
    }
    return false;
}
 29 апр. 2012 г., 12:26
shared_ptr может использоваться только в том случае, если pointee может пережить объект, который его создал.

Я могу придумать несколько способов справиться с этим:

As others suggested, use boost::optional Make the object have a state that indicates it is not valid (Yuk!) Use pointer instead of reference Have a special instance of the class that is the null object Throw an exception to indicate failure (not always applicable)
 25 нояб. 2015 г., 16:38
Наконец, кто-то может порекомендовать шаблон проектирования нулевого объекта. Это действительно должно быть номером один.
 29 апр. 2012 г., 11:59
Объяснение, чтобы пойти вместе с downvote было бы хорошо. Особенно за то, что есть несколько предложений. :)
 23 мая 2017 г., 23:16
Я думаю о & quot; У меня есть специальный экземпляр класса, который является нулевым объектом & quot; потом наткнулся на это.
 30 апр. 2012 г., 11:13
Я собирался предложить: иметь специальный экземпляр класса, который является нулевым объектом. Немного похоже на то, как C # делает это с String.Empty. Просто имейте один статический экземпляр 'null' версия каждого класса. Затем вы можете сравнить возвращаемое значение с «нулевым»; пример.
 30 апр. 2012 г., 11:22
@Neil Да, это может быть удобным подходом в определенных ситуациях. Лично мне это не нравится: как только вы его используете, ваш код не может предположить (тривиально), что объект, который у вас есть, является действительным. Это верно и для моего второго предложения выше.

Почему "кроме использования указателей"? Использование указателейis как вы делаете это в C ++. Если вы не определите некоторые & quot; необязательные & quot; тип, который имеет что-то вродеisNull() Функция, которую вы упомянули. (или использовать существующий, например,boost::optional)

Ссылки разработаны и гарантированы, чтобыnever be null, Задавая вопрос & quot; как мне сделать их нулевыми & quot; бессмысленно. Вы используете указатели, когда вам нужна «ссылка на Nullable».

 29 апр. 2012 г., 14:17
Re & quot; Ссылки разработаны и гарантированы, чтобыnever be null. & Quot; Я еще не сталкивался с реализацией, которая гарантирует, что ссылки не равны нулю. Стандарт не гарантирует, что ссылки никогда не будут нулевыми. Это мандат, а не гарантия. Бремя ложится на программиста. К сожалению, довольно просто (и в высшей степени UB) сделать нулевую ссылку. НО +1. Если вы хотите & quot; ссылку & quot; это может быть нулевым, нужно использовать указатель, а не ссылку.
 29 апр. 2012 г., 15:00
@jalf: Нетрудно обмануть компилятор в создании ссылки NULL (int &n = *static_cast<int*>(NULL);). Однако я бы никогда не предположил, что это хорошая идея. :)
 29 апр. 2012 г., 14:49
@jalf: Мы спорим из-за семантики, но мне все еще нравится слово «мандат» сверх "гарантии". Для меня "гарантия" ложится бременем на поставщика компилятора, в то время как «мандат» возлагает бремя на программиста.
 29 апр. 2012 г., 14:23
@DavidHammen: стандарт в значительной степени говорит о том, что я делал выше: эти ссылки указывают на объект. При этом стандарт гарантирует, что ссылки указывают на объект в допустимом четко определенном коде C ++. Конечно тыcan обманным путем заставит компилятор создать «нулевую ссылку», но тогда программа больше не будет привязана к поведению, указанному в стандарте, и гарантия не распространяется. Зависит от вашей точки зрения, правда. :) Стандарт не гарантирует, что ссылки являются ненулевыми в программах, демонстрирующих UB. Но это гарантирует, что они будут ненулевыми в четко определенных программах. :)
 29 апр. 2012 г., 15:02
@DavidHammen В некотором смысле, вы правы. Но на самом деле есть два способа взглянуть на это. Ничто не мешает программисту попытаться определить «нулевую ссылку», а стандарт этого не делает.forbid Это. Это неmandate что-нибудь. Он просто говорит, что «если вы сделаете это, я не буду ручаться за поведение вашей программы». Другими словами, "в любой программе, которая соблюдает мои правила, я гарантирую, что ссылки не будут нулевыми". Но, как вы говорите, это довольно бессмысленный обман.

Ваш ответ на вопрос