2009-10-12 9 views
9

Estoy buscando una manera fácil de crear una matriz de cadenas en tiempo de compilación. Para una prueba, he creado una clase llamada Strings que tiene los siguientes miembros:Abusar del operador de coma

Strings(); 
Strings(const Strings& that); 
Strings(const char* s1); 
Strings& operator=(const char* s1); 
Strings& operator,(const char* s2); 

Usando esto, puedo compilar correctamente código como este:

Strings s; 
s="Hello","World!"; 

La parte s="Hello" invoca el operator= que devuelve a Strings& y luego se llama al operator, para "World!".

Lo que no puedo ir a trabajar (en MSVC, no he probado otros compiladores aún) es

Strings s="Hello","World!"; 

Me asumir aquí que Strings s="Hello" llamarían el constructor de copia y luego todo se comportarían lo mismo que el primer ejemplo. Pero me sale el error: error C2059: syntax error : 'string'

Sin embargo, esto funciona bien:

Strings s="Hello"; 

así que sé que el constructor de copia hace al menos trabajo para una cadena. ¿Algunas ideas? Me gustaría que el segundo método funcione solo para que el código sea un poco más limpio.

+8

Wow, he escuchado muchas bromas sobre la sobrecarga del operador de coma. Nunca esperé que alguien realmente ** lo hiciera **. –

+2

@Carl: ¿Qué hay de http://www.boost.org/doc/libs/1_40_0/libs/assign/doc/index.html#intro (muy parecido a algo que OP quiere) – UncleBens

+0

* estremecimiento * Poner las uñas en su propio ataúd. ¿Cómo vas a depurar esto cuando empiece a ir mal? –

Respuesta

14

Creo que la coma en su segundo ejemplo no es el operador de coma sino el elemento de gramática para varias declaraciones de variables.

por ejemplo, de la misma manera que se puede escribir:

int a=3, b=4 

Me parece que está esencialmente escribiendo:

Strings s="Hello", stringliteral 

Así el compilador espera que el artículo después de la coma para ser el nombre de una variable, y en su lugar ve una cadena literal y anuncia un error. En otras palabras, el constructor se aplica a "Hola", pero la coma después es no el operador de coma de Cadenas.

Por cierto, el constructor no es realmente un constructor de copia. Crea un objeto Strings a partir de un parámetro de cadena literal ... El término constructor de copia generalmente se aplica al mismo tipo.

+2

Con el riesgo de ser tediosamente pedante, 'Strings s =" Hello ";' es equivalente a 'Strings s = Strings (" Hello ");'. Requiere que el copy ctor sea invocable, e incluso puede llamarlo, para construir 's' desde un objeto temporal. Pero la optimización para reemplazarla con inicialización directa usando el constructor 'const char *' para 's', sin ningún tipo de temporal, está permitida y es común. –

+0

Duh, no puedo creer que no lo haya visto. – miked

8

No recomendaría este tipo de API. Continuará descubriendo casos que no funcionan como se esperaba, ya que coma es el operador con la precedencia más baja.Por ejemplo, este caso no va a funcionar bien:

if ("Hello","world" == otherStrings) { ... } 

Usted puede ser capaz de hacer las cosas de trabajo si utiliza soportes cada vez que todo el conjunto de cadenas, como esto:

Strings s=("Hello","World!"); 

Y mi ejemplo anterior sería el siguiente:

if (("Hello","world") == otherStrings) { ... } 

que probablemente se puede hacer para trabajar, pero la sintaxis abreviada es probablemente no vale la semántica difíciles que vienen con él.

+3

¿Se puede hacer que funcione? El problema que preveo es que no se puede sobrecargar 'operator' para' const char * ', entonces' ("Hello", "world") 'es lo mismo que' "world" '. –

+0

Supuse que puede sobrecargar el operador, (const char *, const char *). ¿Hay alguna razón por la que eso no se puede hacer? No hago mucha programación en C++, para ser honesto. –

0

Se podría utilizar un conjunto de punteros a carácter

Strings::Strings(const char* input[]); 

const char* input[] = { 
    "string one", 
    "string two", 
    0}; 

Strings s(input); 

y dentro del constructor, iterar a través de los punteros hasta llegar a la nula.

+0

O plantilla el constructor: 'plantilla Cadenas (const char * (y entrada) [N]) {for (int i = 0; i

+0

Oh, o por supuesto, la plantilla del constructor podría llamar a otra función similar a 'vector :: insert (iterator, InputIterator, InputIterator)', para evitar que se genere código duplicado si lo llamas con diferentes longitudes en diferentes lugares. –

0

¡Si tienes C++ 0x, tienen el nuevo inializer lists para esto! Ojalá pudieras usar esos. Por ejemplo:

std::vector<std::string> v = { "xyzzy", "plugh", "abracadabra" }; 
std::vector<std::string> v{ "xyzzy", "plugh", "abracadabra" }; 
0

Si el único trabajo de Strings es almacenar una lista de cadenas, a continuación, boost::assign podría hacer el trabajo mejor con los contenedores estándar, creo :)

using namespace boost::assign; 
vector<string> listOfThings; 
listOfThings += "Hello", "World!"; 
1

Es posible hacer este trabajo, para una definición suficientemente holgada de "trabajo". Aquí hay un ejemplo de trabajo que escribí en respuesta a una pregunta similar hace algunos años. Fue divertido como un desafío, pero no lo usaría en el código real:

#include <iostream> 
#include <algorithm> 
#include <iterator> 
#include <vector> 

void f0(std::vector<int> const &v) { 
    std::copy(v.begin(), v.end(), 
     std::ostream_iterator<int>(std::cout, "\t")); 
    std::cout << "\n"; 
} 

template<class T> 
class make_vector { 
    std::vector<T> data; 
public: 
    make_vector(T const &val) { 
     data.push_back(val); 
    } 

    make_vector<T> &operator,(T const &t) { 
     data.push_back(t); 
     return *this; 
    } 

    operator std::vector<T>() { return data; } 
}; 

template<class T> 
make_vector<T> makeVect(T const &t) { 
    return make_vector<T>(t); 
} 

int main() { 
    f0((makeVect(1), 2, 3, 4, 5)); 
    f0((makeVect(1), 2, 3)); 
    return 0; 
} 
Cuestiones relacionadas