2010-08-07 19 views
11

tuple en boost y TR1/C++ 0x proporciona un método conveniente (para el escritor de la función) para devolver dos valores de una función, aunque parezca daños una característica importante de la lengua de la persona que llama: la capacidad de utilizar simplemente la función para inicializar una variable:Cómo inicializar con valores de retorno múltiples en C++ (0x)

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

pero para:

tuple<T,U> sad(); 

que o bien tienen que renunciar a la posibilidad de escoger una nombre significativo para nuestros valores de devolución, y use get<n>() en todas partes:

const auto two_unrelated_things(sad()); 

o hacer un temporal:

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

o cambiar de inicialización de la asignación, que sólo funciona cuando los tipos son asignables, y se rompe 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(); 

o hacer algo antinatural a una clase local:

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 

¿Hay una manera mejor? Estoy usando las construcciones compatibles con VC10 anteriores, ¿habría algo en C++ 0x completo o aumentaría la ayuda?

Lo ideal sería:

  • me permite utilizar la inicialización, no sólo la asignación
  • dejar que el persona que llama recoger los nombres de los
  • no hacer copias adicionales devueltos-en variables
  • trabajo para las variables de la pila y los miembros de la clase
  • posiblemente sea una biblioteca de plantillas loca, pero tenga una sintaxis sana para el que llama y el escritor de funciones
+2

Pregunta interesante, aunque no veo cómo podría definir variables de diferentes tipos en una sola expresión. - Creo que la opción "o hacer una temporal" podría estar bien, si cambias las variables nombradas a referencias (evitando copiar). – UncleBens

+0

Buen punto sobre las referencias: creo que es una solución para las variables de pila. He intentado hacer lo mismo en una clase: clase C {public : C sr () (triste()), un (obtener <0> (SR)), dos (obtener <1> (SR)) {} const T & one; const U & two; privado: tupla sr; } Pero parece que en VC10, C es dos punteros más grande que la tupla, no es un gran problema sino un poco cojo: ¿no sería legal que el compilador reconozca que las referencias son alias y no asignan espacio en la instancia para ellos? ¿No es por eso que los indicadores de referencias son ilegales en primer lugar? – BCoates

+0

Con una clase, si los datos se almacenan como una tupla, podría simplemente proporcionar métodos de acceso con nombre, que llaman a los respectivos 'obtener '. Dudo que haya una solución basada en una "plantilla loca", porque el lenguaje central simplemente no parece respaldar lo que estás pidiendo. Tal vez podrías reducir el número de caracteres que tienes que escribir con macros ... – UncleBens

Respuesta

2
std::tuple<Type1, Type2> returnValue = sad(); 
Type1& first = std::get<0>(returnValue); 
Type2& second = std::get<1>(returnValue); 

no estoy seguro de lo que los cuartos medios de bala, pero que satisface todos los demás.

* edit: Basado en su comentario anterior, descubrí lo que quería decir con la cuarta viñeta.

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; 
} 

Modificar según sea necesario.

No podría usar std::tuple en absoluto si los valores devueltos son tan independientes que debe dividirlos para que se puedan usar razonablemente. La gente ha sobrevivido durante años devolvió struct s con campos razonablemente nombrados, o al aceptar los parámetros de referencia para la salida.

Como un lado, pareces estar enamorado de auto. No seas. Es una gran característica, pero esto es no la forma en que debe ser utilizado. Su código terminará ilegible si no especifica tipos de vez en cuando.

+0

Cuando pienso en el bit de "clase de miembros", ¿es esta una situación en la que puede eludir todas las copias? Creo que vas a terminar copiando 'valor' al menos una vez bajo cualquier circunstancia, lo que obviamente requerirá copiar a sus miembros al menos una vez. Así que probablemente también deba almacenar los valores directamente y olvidarse de almacenar la tupla. –

+0

Estoy de acuerdo con no usar 'std :: tuple' cuando no es apropiado - Solo me preocupa que se vuelva idiomático y haga que el código sea menos comprensible y esté lleno de' obtener (algo) ' Dicho eso , parece que me preocupo demasiado por las copias adicionales: 'tie (a, b) = sad()' en un constructor elides con éxito todas las copias de los miembros de la tupla cuando sad() hace 'return make_tuple (...)' . Tal vez la lección es "los miembros de datos' 'const' son una molestia". – BCoates

+1

@bcoates: respecto al uso erróneo y excesivo de la tupla. La biblioteca estándar es un poco culpable cuando representa rangos como 'pair '. OTOH, boost está empleando una alternativa superior: 'iterator_range ' con un método 'begin()' y 'end()'. Un rango no es un par abstracto, 'primero' y 'segundo' tienen un significado concreto! - No creo que uno deba ir muy tuple-feliz en código no genérico. – UncleBens

Cuestiones relacionadas