Как инициализировать с несколькими возвращаемыми значениями в С++ (0x)

tuple в boost и TR1/С++ 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-совместимые конструкции выше, будет ли что-нибудь в полной версии С++ 0x или boost?

В идеале это:

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

Ответ 1

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. Не будь. Это отличная функция, но это не так, как ее следует использовать. Ваш код будет нечетким, если вы не укажете типы время от времени.