стандартный шаблон для возврата значения с логическим флагом результата - PullRequest
0 голосов
/ 13 ноября 2018

Поскольку я начинаю использовать преимущества структурированных привязок C ++ 17 и, если операторы оператора init для более элегантной функции сообщают о результатах и ​​проверяют их, я начал делать следующее, в соответствии с C ++ Core Guideline F21:

std::pair<bool, int>Foo()
{
    return {true, 42}; //true means that function complete with no error and that 42 is a good value
}

void main(void)
{
    if (auto [Result, Value] = Foo(); Result)
    {
        //Do something with the return value here
    }
}

Тогда, конечно, я подумал, что было бы неплохо иметь повторно используемый шаблон для таких типов возврата, чтобы никто не дублировал часть bool пары:

template <typename T> using validated = std::pair<bool,T>;

validated<int> Foo()
{
    return {true, 42};
}

void main(void)
{
    if (auto [Result, Value] = Foo(); Result)
    {
        //Do something with the return value here
    }
}

Это прекрасно работаетдля меня, но теперь мне интересно, есть ли какой-то стандартный эквивалент этого шаблона, чтобы мне не пришлось заново изобретать колесо и определять его самому.Похоже, что значение произвольного типа в сочетании с флагом достоверности было бы полезной конструкцией, но я не смог найти ничего в стандартной библиотеке.Я что-то упустил?

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

Вас могут заинтересовать предлагаемые std::expected.

Его интерфейс следует std::optional довольно близко.Основным преимуществом expected<T, E> над optional<T> является возможность переноса ошибки:

enum class errc {err1, err2, err3};

std::expected<int, errc> Foo()
{
  if (/* error condition 1 */)  return std::unexpected(errc::err1);

  // ... checking other error conditions

  return 42;  // no error condition (42 is a good value)
              // implicit conversion from `int` to `expected<int, errc>`
              // avoid boilerplate code
}

int main()
{
  auto q = Foo();

  if (q)
  {
    // Do something with the return value here
  }
}

Вы также можете взглянуть на:


В качестве примечания main() должен вернуть int.

0 голосов
/ 13 ноября 2018

std :: необязательный - это именно то, о чем вы спрашиваете.Это даже в описании:

Распространенным вариантом использования для опции является возвращаемое значение функции, которая может завершиться ошибкой.В отличие от других подходов, таких как std::pair<T,bool>, опционально хорошо обрабатывает дорогостоящие объекты и более удобочитаем, поскольку намерение выражается явно.

if из примера будетВыглядит немного проще:

#include <optional>
#include <iostream>

std::optional<int> Foo(bool fail)
{
    if (!fail) return {42};
    return {};
}

void process(bool fail) {
    if (auto val = Foo(fail)) {
        std::cout << val.value() << '\n';
    } else {
        std::cout << "No value!\n";
    }    
}

int main() {
    std::optional<int> oi;
    process(true);
    process(false);
}

Если вы действительно хотите использовать Value явно, то вы всегда можете распаковать его по ссылке на успешную ветку, т.е. auto Value = val.value();

Вам нужноостерегаться некоторых предостережений.2 от макушки головы:

  1. Производительность: Почему конструкция std :: необязательна дороже, чем std :: pair? хотя для данного примера современный лязг с -O3 выглядит довольно убедительно

    Примечание: static было добавлено для process для краткости -для предотвращения генерации версии для внешних ссылок.

  2. Возвращает false, если объект был создан по умолчанию.Это может удивить некоторых, так как конструкция по умолчанию optional не строит базовое значение по умолчанию.

РЕДАКТИРОВАТЬ: После комментариев я решил явно заявить, что нет ничего похожего на псевдоним типадля pair<T,bool> или аналогичного, совместимого со стандартной библиотекой.Нелегко доказать, что чего-то не существует, но если бы был такой тип, стандартная библиотека наверняка использовала бы его в объявлении insert, это не так;следовательно, я настоятельно подразумеваю, что вокруг него нет семантической оболочки.

...