Подтвердить что ты не робот

Списки инициализаторов и RHS операторов

Я не понимаю, почему списки инициализаторов не могут использоваться в RHS оператора. Рассмотрим:

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

Последний Clang (gcc также) жалуется:

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

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

Почему стандарт С++ запрещает это? Или иначе, почему это происходит, а не

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

?

4b9b3361

Ответ 1

В самом деле, окончательная версия С++ 11 не позволяет использовать списки инициализаций с правой стороны (или, если нужно, с левой стороны) двоичного оператора.

Во-первых, списки инициализаторов не являются выражениями, как определено в § 5 Стандарта. Аргументы функций, а также двоичных операторов обычно должны быть выражениями, а грамматика для выражений, определенных в § 5, не включает синтаксис списков перекрестных списков (то есть чистых списков инициализаторов; например, bar {2,5,"hello",7} - это выражение).

Чтобы иметь возможность удобно использовать чистые списки инициализаторов, стандарт определяет различные исключения, которые суммируются в следующей (ненормативной) заметке:

§8.5.4/1 [...] Примечание: можно использовать инициализацию списка
    - как инициализатор в переменной определении (8.5)
- как инициализатор в новом выражении (5.3.4)
- в заявлении о возврате (6.6.3)
- как аргумент функции (5.2.2)
- в качестве индекса (5.2.1)
- в качестве аргумента для вызова конструктора (8.5, 5.2.3)
- как инициализатор для нестатического элемента данных (9.2)
- в mem-инициализаторе (12.6.2)
- в правой части задания (5.17)
[...]

Четвертый пункт выше явно разрешает чистые инициализационные списки в качестве аргументов функции (поэтому работает operator<<(baz, {1, -2, "foo", 4, 5});), пятый - в выражении в нижнем индексе (т.е. как аргумент operator[], например mymap[{2,5,"hello"}] является законным), а последний элемент разрешает их в правой части назначений (но не общих двоичных операторов).

Существует не такое исключение для бинарных операторов, например +, * или <<, поэтому вы не можете поместить чистый список инициализаторов (то есть тот, которому не предшествует typename) по обе стороны от них.

Что касается причин для этого, проект проект/дискуссионный документ N2215 от Stroustrup и Dos Reis с 2007 года предусматривает много понимания многих проблем с списками инициализаторов в различных контекстах. В частности, существует раздел о бинарных операторах (раздел 6.2): ​​

Рассмотрим более общее использование списков инициализаторов. Например:

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

Когда мы рассматриваем операторы как синтаксический сахар для функций, мы, естественно, рассмотрим вышеописанное значение

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

Поэтому естественно распространять использование списков инициализаций в выражения. Существует много применений, когда списки инициализаторов в сочетании с операторами являются "естественными" обозначениями.
Однако нетривиально написать грамматику LR (1), которая позволяет произвольно использовать списки инициализаторов. Блок также начинается с {, поэтому включение списка инициализаторов в качестве первого (самого левого) объекта выражения приведет к хаосу в грамматике.
Тривиально разрешить списки инициализаторов в качестве правого операнда двоичных операторов, в индексы и аналогичные отдельные части грамматики. Реальная проблема заключается в том, чтобы позволить ;a={1,2}+b; как присваивать-инструкцию без разрешения ;{1,2}+b;. Мы подозреваем, что включение списков инициализаторов как правых, но и [sic] в качестве левых аргументов большинству операторов является слишком большим количеством kludge, [...]

Другими словами, списки инициализаций не включены с правой стороны, потому что они не включены с левой стороны, и они не включены с левой стороны, потому что это могло бы поставить слишком большая проблема для парсеров.

Интересно, можно ли упростить эту проблему, выбрав другой символ вместо фигурных скобок для синтаксиса списка инициализаторов.