Как инициализировать с несколькими возвращаемыми значениями в C ++ (0x) - PullRequest
11 голосов
/ 07 августа 2010

tuple в boost и TR1 / c ++ 0x предоставляет удобный (для автора функции) метод для возврата двух значений из функции - однако, похоже, он повреждает одну важную функцию языка для вызывающей стороны : возможность просто использовать функцию для инициализации переменной:

T happy();
const auto meaningful_name(happy()); // RVO means no excess copies

но для:

tuple<T,U> sad();

мы должны либо отказаться от возможности выбрать значимое имя для наших возвращаемых значений, и использовать get<n>() везде:

const auto two_unrelated_things(sad());

или сделать временный:

const auto unwanted_named_temporary(sad());
const auto one_name(get<0>(unwanted_named_temporary));
const auto two_name(get<1>(unwanted_named_temporary));

или переключение с инициализации на присвоение, которое работает только тогда, когда типы могут быть назначены, и прерывается auto:

tuple_element<0, decltype(sad())>::type one_mutable; // there might be a less
tuple_element<1, decltype(sad())>::type two_mutable; // verbose way
tie(one_mutable,two_mutable) = sad();

или сделать что-то неестественное для местного класса:

const struct ugh { 
  ugh( decltype(sad()) rhs ) : one_name(get<0>(rhs)), two_name(get<1>(rhs)) {}
  const tuple_element<0, decltype(sad())>::type one_name;
  const tuple_element<1, decltype(sad())>::type two_name;
} stuff(sad()); // at least we avoid the temporary and get initialization

Есть ли лучший способ? Я использую VC10-совместимые конструкции выше, поможет ли что-нибудь в полной версии c ++ 0x или boost?

В идеале это будет:

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

1 Ответ

2 голосов
/ 07 августа 2010
std::tuple<Type1, Type2> returnValue = sad();
Type1& first = std::get<0>(returnValue);
Type2& second = std::get<1>(returnValue);

Я не уверен, что означает ваша четвертая пуля, но это удовлетворяет всем остальным.

* edit: Основываясь на вашем комментарии выше, я понял, что вы имели в виду под четвертой пулей.

struct Object {
    Object(const std::tuple<Type1, Type2>& t) : value(t) { }
    Type1& First() { return std::get<0>(value); }
    Type2& second() { return std::get<1>(value); }
private:
    std::tuple<Type1, Type2> value;
}

Изменить при необходимости.

Вы также можете вообще не использовать std::tuple, если возвращаемые значения настолько не связаны, что вам нужно разделить их, чтобы их можно было разумно использовать. В течение многих лет люди возвращали struct с полями с разумными именами или принимали эталонные параметры для вывода.

Кстати, вы, кажется, влюблены в auto. Не будь Это отличная возможность, но это не , как это должно быть использовано. Ваш код станет неразборчивым, если вы не будете указывать типы время от времени.

...