Подтвердить что ты не робот

Python Нет функции возврата, имитирующей С++

Мне нравится функция в Python, которая может возвращать None, когда она не находит правильное возвращаемое значение. Например:

def get(self, key):
    if key in self.db:
        return self.db[key]
    return None

Мне нужно реализовать ту же функцию в С++. Я думаю о некоторых возможностях.

Возвращает true/false, когда true получает значение из ссылки или указателя

bool get(string key, int& result)
{
    if (in(key, db)) {
        result = db[key];
        return true;
    }
    return false;
}

Выслать сообщение об ошибке "Нет случая"

int get(string key) throw (int)
{
    if (in(key, db)) {
        result = db[key];
        return result;
    }

    throw 0;
}

try {
    ....
}
catch (int n)
{
    cout << "None";
}

Использовать пару

pair<bool, int> getp(int i)
{
    if (...) {
        return pair<bool, int>(true, 10);
    }
    return pair<bool,int>(false, 20);
}

pair<bool, int> res = getp(10);
if (res.first) {
    cout << res.second;
}

Какой из них обычно используется в С++? Есть ли другие способы сделать это на С++?

4b9b3361

Ответ 1

Обычный способ С++ для этого (обратите внимание: С++ не является Python) должен возвращать итераторы из таких функций и возвращать end(), когда элемент не может быть найден.

Если вы хотите использовать возвращаемые значения нетератора, используйте boost::optional и возвращайте boost::none, когда вы вернете Python None.

Определенно не используйте throw, если вы не ожидаете, что при нормальном выполнении никогда не будет иметь место ошибка.

Ответ 2

Я получил хорошее/плохое возвращаемое значение, используя небольшой настраиваемый Checked шаблонный класс. Мой фактический класс был немного более полным, включая операторы присваивания, строки причины ошибки и специализации для ссылочных типов и т.д., Поэтому я не использовал boost::optional<T>. Я мог бы опубликовать полный класс, если есть интерес.

Общий смысл класса таков:

static const class Bad {} None;

template<typename ValueType>
class Checked
{
public:
    // Constructor for good value.
    Checked(ValueType x)
        : value(x), valid(true)
    {}

    // Constructor for bad value.
    Checked(Bad)
        : value(), valid(false)
    {}

    operator ValueType(void) const
    {
        if (!valid)
            ;//assert or throw...

        return value;
    }

    ValueType value;
    bool valid;
};

Это можно использовать так:

Checked<int> Divide(int numerator, int denominator)
{
    if (denominator == 0)
        return Bad(); // or None;

    return numerator / denominator; // Automatically uses the "good value" constructor
}

или

Checked<int> result = Divide(4, 5);
if (result.valid)
    std::cout << result; // or result.value
else
    std::cout << "Bad!";

Этот подход часто более эффективен, чем ссылочный подход из-за оптимизации возвращаемого значения.

Ответ 3

Я думаю, что разные проекты на С++ используют разные стандарты, но Return true/false, которые вы упомянули, может быть наиболее распространенным способом на С++, хотя некоторые люди предпочитают возвращать false при успешном выполнении, в то время как другие возвращают true на успех. В других случаях, если значение, которое вы хотели бы получить, является указателем, тогда возврат значения null является еще одним распространенным способом в С++.

Например, если вы работаете над проектами, связанными с Microsoft, то наиболее распространенным способом является возвращение HRESULT, что тип возврата, введенный Microsoft.

В linux функции обычно возвращают 0 при успехе, а ненулевое значение указывает код ошибки. (вы можете найти эту дискуссию полезной).

Ответ 4

Я бы сказал, что эти три метода очень распространены в С++.

Само собой разумеется, что если тип возврата уже может иметь какое-то "недопустимое" или "зомбическое" состояние (например, как указатель NULL или номер NaN), то это может быть просто самым простым вещь для использования.

"Принимать выходной параметр по ссылке и возвращать код ошибки" - это более традиционный способ поведения в стиле C, который, конечно, очень распространен. Традиция состоит в том, чтобы вернуть 0 на успех и некоторый код ошибки при сбое (любое ненулевое значение).

"Выбрасывать исключение, если вы не можете вернуть значение", обычно имеет смысл, если вы принимаете исключения в своем коде. Это очень распространено, но не общепринятое (не все любят или используют исключения для тех же целей).

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

"вернуть пару bool и значение", я бы сказал, было менее распространено, но все же я видел это много раз. С принятием кортежей (boost::tuple или std::tuple (С++ 11)) и с использованием уровней (boost::tie или std::tie (С++ 11)) вся идея возврата нескольких значений из функция (как и многие языки) все более привлекательна и используется на практике.

Среди других вариантов у вас есть boost::optional<T>, чье имя довольно самоочевидно (в основном, третий вариант (пара), завернутый в более красивый пакет). И у вас также может быть шаблон Alexandrescu Expected, который дает вам гибрид из всех трех вариантов, чтобы получить возвращаемое значение в комплекте с флагом, чтобы узнать, действительно ли оно или нет, и в комплекте с исключением, описывающим, почему он не может произвести значение, которое будет автоматически выбрано, если вы попытаетесь прочитать недопустимое значение. Однако для шаблона требуются функции С++ 11.

Ответ 5

При возврате указателя я могу использовать reinterpret_cast для возврата NULL.

class A
{
};

A* a(int i)
{
    if (i == 0) return new A();
    return reinterpret_cast<A*>(NULL);
}
int main(int argc, char *argv[]) {
    A* result = a(1); // result is NULL
    if (result == NULL) {
        cout << "NULL returned";
    }
    result = a(0);
    if (result != NULL) {
        cout << "NON NULL returned";
    }
}