2012-07-10 11 views
34

No entiendo por qué las listas de inicializadores no se pueden usar en el RHS de un operador. Considere:Lista de inicializadores y RHS de operadores

class foo { }; 

struct bar 
{ 
    template<typename... T> 
    bar(T const&...) { } 
}; 

foo& operator<<(foo& f, bar const&) { return f; } 

int main() 
{ 
    foo baz; 
    baz << {1, -2, "foo", 4, 5}; 

    return 0; 
} 

La última Clang (gcc también) se queja:

clang.cc:14:9: error: initializer list cannot be used on the right hand side of operator '<<' 
    baz << {1, -2, "foo", 4, 5}; 
    ^~~~~~~~~~~~~~~~~~~~~ 

    ^~~~~~~~~~~~~~~~ 

¿Por qué el estándar C++ prohibir esto? O dicho de otro modo, ¿por qué esto falla a diferencia de

baz << bar{1, -2, "foo", 4, 5}; 

?

+9

Porque no has sobrecargado 'operator <<' para tomar una 'initializer_list <>' en el RHS ... ¿Cuál es tu pregunta real? – ildjarn

+0

Esperaba que esto fuera equivalente a 'baz << bar {1, 2, 3, 4, 5};', pero parece que no hay conversión. – mavam

+3

Si ese es el comportamiento que desea, tal vez debería intentar darle a 'bar' un constructor no explícito que tome una sola' initializer_list <> '. – ildjarn

Respuesta

45

De hecho, la versión final de C++ 11 no permite el uso de listas de inicializadores en el lado derecho (o en el lado izquierdo, para el caso) de un operador binario.

En primer lugar, listas de inicializadores no son expresiones como se define en el §5 de la Norma. Los argumentos de las funciones, así como de los operadores binarios, generalmente tienen que ser expresiones, y la gramática para las expresiones definidas en el §5 no incluye la sintaxis para las listas de inicio de llaves (es decir, listas de inicializadores puras; tenga en cuenta que un nombre de tipo seguido de una lista de inicio de llaves, como bar {2,5,"hello",7} es una expresión, sin embargo).

Con el fin de ser capaz de utilizar inicializador listas puros convenientemente, la norma define varias excepciones, que se resumen en la nota siguiente (no normativo):

§8.5.4/1 [...] Nota: Lista-inicialización se puede utilizar
- como el inicializador en una definición de variable (8,5)
- como el inicializador en una nueva expresión (5.3.4)
- en una instrucción de retorno (6.6.3)
- como una función argumen t (5.2.2)
- como un subíndice (5.2.1)
- como argumento para una invocación de constructor (8.5, 5.2.3)
- como inicializador para un miembro de datos no estático (9.2)
- en una mem-inicializador (12.6.2)
- en el lado derecho de una asignación (5,17)
[...]

el cuarto punto anterior permite explícitamente initializer- pura listas como argumentos de funciones (que es por lo que operator<<(baz, {1, -2, "foo", 4, 5}); funciona), el quinto lo permite en expresiones de subíndices (es decir, como argumento de operator[], p. mymap[{2,5,"hello"}] es legal), y el último elemento les permite en el lado derecho de asignaciones (pero no operadores binarios generales).

Hay hay tal excepción para los operadores binarios como +, * o <<, por lo tanto, no se puede poner una lista de inicialización puro (es decir, uno que no está precedido por un nombre de tipo) a ambos lados de ellos.

En cuanto a las razones de esta , un draft/discussion paper N2215 por BS y Dos Reis desde 2007 proporciona un montón de información sobre muchos de los problemas con inicializador listas en varios contextos.Específicamente, hay una sección sobre operadores binarios (sección 6.2): ​​

Considere usos más generales de las listas de inicializadores. Por ejemplo:

v = v+{3,4}; 
v = {6,7}+v; 

Cuando consideramos los operadores como el azúcar sintáctica para las funciones, es natural considerar lo anterior equivale a

v = operator+(v,{3,4}); 
v = operator+({6,7},v); 

lo tanto, es natural extender el uso de listas de inicializador de expresiones. Hay muchos usos en los que las listas de inicializadores combinadas con operadores son una notación "natural".
Sin embargo, no es trivial escribir una gramática LR (1) que permita el uso arbitrario de las listas de inicializadores. Un bloque también comienza con un {permitiendo así una lista de inicializadores ya que la primera entidad (a la izquierda) de una expresión llevaría al caos en la gramática.
Es trivial permitir listas de inicializadores como el operando de la derecha de operadores binarios, en los subíndices , y partes aisladas similares de la gramática. El verdadero problema es permitir ;a={1,2}+b; como una declaración de asignación sin permitir también ;{1,2}+b;. Sospechamos que permite inicializador enumera como de la derecha, pero tampoco [sic] como argumentos de la mano izquierda a la mayoría de los operadores es demasiado de un kludge, [...]

En otras palabras, de inicializador Listas de no están habilitados en el lado derecho porque no están habilitados en el lado izquierdo, y no están habilitados en el lado izquierdo porque eso representaría un desafío demasiado grande para los analizadores.

Me pregunto si el problema podría haberse simplificado eligiendo un símbolo diferente en lugar de llaves para la sintaxis de la lista de inicializadores.

+2

Un símbolo diferente podría haber hecho más cosas posibles, pero '{}' es una extensión tan natural de los inicializadores de matriz y de los inicializadores de estructura POD heredados de C89. – aschepler

+1

Gracias por esta explicación: busqué el operador de telémetro 'true? {1,2,3}: {4,5,6}' - así que esto no es solo un problema de operadores binarios ... – PiotrNycz

+1

@PiotrNycz Eso puede no ser un problema de análisis, pero un problema de deducción de tipo. Ambas alternativas del operador ternario necesitan acordar un tipo común, y algo también tiene que determinar la categoría del valor del resultado. Los tirantes oscurecen el significado del programa. Es mejor en '?:' Ser explícito y poner el nombre de tipo antes del '{', y esto no sacrifica ningún poder expresivo. – Potatoswatter

Cuestiones relacionadas