2012-07-10 12 views
34

Nie rozumiem, dlaczego listy inicjalizacyjne nie mogą być używane w RHS operatora. Rozważmy:Listy inicjatorów i RHS operatorów

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

Najnowszy Clang (GCC również) narzeka:

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

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

Dlaczego miałby standard C++ tego zabronić? Albo inaczej mówiąc, dlaczego to nie w przeciwieństwie do

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

?

+9

Ponieważ nie przeładowałeś 'operatora <<' do zrobienia 'initializer_list <>' na RHS ... Jakie jest twoje aktualne pytanie? – ildjarn

+0

Miałem nadzieję, że jest to odpowiednik 'baz << bar {1, 2, 3, 4, 5};', ale wygląda na to, że nie ma żadnej konwersji. – mavam

+3

Jeśli takie zachowanie chcesz, może powinieneś spróbować podać 'bar' nie-jawny konstruktor, który bierze pojedynczą' initializer_list <> '. – ildjarn

Odpowiedz

45

Rzeczywiście ostateczna wersja C++ 11 nie umożliwia korzystania z list inicjalizujących po prawej stronie (lub lewej stronie, jeśli o to chodzi) operatora binarnego.

Po pierwsze, listy inicjalizacyjne nie są wyrażeniami zdefiniowanymi w §5 Standardu. Argumenty funkcji, jak również operatorów binarnych, muszą być generalnie wyrażeniami, a gramatyka dla wyrażeń zdefiniowanych w §5 nie zawiera składni dla listek inicjujących brace (tj. Czystych list inicjalizujących; zauważ, że nazwa_tabliczna następnie lista inicjująca nawias klamrowy, taka jak bar {2,5,"hello",7}, jest wyrażeniem).

Aby móc użyć czystego inicjator listami Korzystnie, średnia definiuje różne wyjątków, które są przedstawione w poniższej (nienormatywnej) Uwaga:

§8.5.4/1 [...] Uwaga: Lista inicjalizacja może być stosowany
- jako inicjator w definicji zmiennej (8,5)
- jako inicjator w nowej ekspresji (5.3.4)
- w instrukcji return (6.6.3)
- w funkcji argumen t (5.2.2)
- jako indeks (5.2.1)
- jako argument do wywołania konstruktora (8.5, 5.2.3)
- jako inicjator dla niż statyczna składowa danych (9.2)
- w mEM inicjatora (12.6.2)
- po stronie prawej przydziału (5,17)
[...]

czwarty element powyżej wyraźnie umożliwia czysty initializer- list jako argumenty funkcji (dlatego działa operator<<(baz, {1, -2, "foo", 4, 5});), piąty pozwala na to w wyrażeniach dolnych (np. jako argument z operator[], np. mymap[{2,5,"hello"}] jest legalny), a ostatni element umożliwia im po prawej stronie przypisania (ale nie ogólne operatory binarne).

Jest ma takiego wyjątku dla operatorów binarnych jak +, * lub <<, stąd nie można umieścić czystą listę initializer (to znaczy taki, który nie jest poprzedzony TypeName) po obu stronach z nich.

Co do przyczyn tego, A draft/discussion paper N2215 przez Stroustrup i Dos Reis od 2007 zapewnia wiele wgląd w wiele problemów z initializer listach w różnych kontekstach.W szczególności, istnieje sekcja operatorów binarnych (sekcja 6.2): ​​

Rozważmy bardziej ogólnych zastosowań list initializer. Na przykład:

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

Gdy weźmiemy pod uwagę operatorów jak cukier dla funkcji składniowej, naturalnie pod uwagę powyższą równowartość

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

Jest więc naturalne, aby rozszerzyć stosowanie list initializer do wyrażeń. Istnieje wiele zastosowań, w których listy inicjalizatorów połączone z operatorami są zapisami "naturalnymi".
Jednak nie jest rzeczą trywialną napisanie gramatyki LR (1), która umożliwia dowolne korzystanie z list inicjalizujących. Blok zaczyna się również od {pozwalając, aby lista inicjalizatorów jako pierwsza (po lewej) jednostka wyrażenia prowadziłaby do chaosu w gramatyce.
To jest trywialne, aby umożliwić listy inicjalizatorów jako prawostronny operand operatorów dwójkowych, w indeksach i podobnych izolowanych częściach gramatyki. Prawdziwym problemem jest dopuszczenie ;a={1,2}+b; jako instrukcji przypisania, bez zezwolenia na ;{1,2}+b;. Podejrzewamy, że pozwalając inicjator wymienia jako prawą rękę, ale ani [sic] jako argumenty lewej ręki do większości operatorów jest zbyt dużo kludge, [...]

Innymi słowy, initializer-list nie są włączone po prawej stronie , ponieważ nie są włączone po lewej stronie: i nie są włączone po lewej stronie, ponieważ spowodowałoby to zbyt duże wyzwanie dla analizatorów składni.

Zastanawiam się, czy problem mógł zostać uproszczony przez wybranie innego symbolu zamiast nawiasów klamrowych dla składni listy inicjalizującej.

+2

Inny symbol mógł uczynić więcej rzeczy możliwymi, ale '{}' jest takim naturalnym rozszerzeniem inicjatorów macierzy i inicjalizatorów POD struct odziedziczonych po C89. – aschepler

+1

Dzięki za wyjaśnienie - szukałem operatora napięciowego "prawda? {1,2,3}: {4,5,6}" - więc nie jest to problem tylko operatorów binarnych ... – PiotrNycz

+1

@PiotrNycz To może nie być problem z analizą, ale problem z odliczaniem typu. Obie alternatywy operatora trójdzielnego muszą uzgodnić wspólny typ, a coś musi również określić kategorię wartości wyników. Klamry zasłaniają znaczenie programu. Lepiej w '?:' Być jawnym i umieścić nazwę typu przed '{', a to nie poświęca żadnej ekspresyjnej mocy. – Potatoswatter

Powiązane problemy