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