Разработка собственного Результата / Либо: Должен ли тип Успеха и Тип ошибки всегда быть предопределенным? - PullRequest
0 голосов
/ 06 ноября 2019

У меня есть реализация Result / Either для c ++. Класс имеет следующий конструктор преобразования типов :

template <typename V, typename E>
class Result {
...
    template<typename X>
    constexpr Result(X&& value);
...
};

Цель этого состоит в том, чтобы включить код, подобный следующему:

Result<SomeType, int> value = SomeType{...};
EXPECT_TRUE(result.isOk());   // TRUE

// Also
Result<SomeType, int> value = 10;
EXPECT_TRUE(result.isError());   // TRUE

Это важно для возврата функциизначения:

Result<SuccessType, int> func() {
  SuccessType result{...};
...
  return result;
}
...

EXPECT_TRUE(func().isOk());

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

Итак, вопросы таковы: - Считаете ли вы, что полезно разрешать одни и те же типы для значения Value и Error? Например: Result<MyTypeA, MyTypeA> r{}; У вас есть пример, когда типы ошибок и типов успеха совпадают, и это полезно?

Мотивация для этого вопроса заключается в том, что я могу избавиться от конструктора шаблона <>но только если V и E не являются одинаковыми типами:

template<class V, class E>
struct Result {
    constexpr result(V&& value);    
    constexpr result(E&& value);
...
  • Кроме того, считаете ли вы, что на практике достаточно иметь только конструктор с r-значением? Я не могу думать, что Result <> не является возвращаемым значением - и, следовательно, нет смысла не перемещать значение из промежуточного объекта.

1 Ответ

0 голосов
/ 06 ноября 2019

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

Если вы либо асимметричный , либо, как результат, вы хотите неявно конвертировать из избранного тип. Из непривлекательного типа вы хотите конвертировать только явно.

template<class X>
struct Error {
  X x;
};

template<class V, class E>
struct Result {
  Result( V&& v );
  Result( Error<E> e );
  // or even:
  template<class O,
    std::enable_if_t< std::is_convertible_v<O, E>, bool > = true
  >
  Result( Error<O> e );
};

Использование выглядит так:

Result<SomeType, int> value = SomeType{...};
EXPECT_TRUE(result.isOk());   // TRUE

// Also
Result<SomeType, int> value = Error{10};
EXPECT_TRUE(result.isError());   // TRUE

И:

Result<SuccessType, int> func() {
  SuccessType result{...};
...
  return result;
} 

работает, как и:

Result<SuccessType, int> func() {
  return Error{10};
} 

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

...