Списки инициализаторов и 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};
^ ~~~~~~~~~~~~~~~~~~~~
^ ~~~~~~~~~~~~~~~
Почему стандарт C++ запрещает это? Или по-другому, почему это не так, в отличие от
baz << bar{1, -2, "foo", 4, 5};
?
1 ответ
Действительно, окончательная версия C++11 не позволяет использовать списки инициализаторов в правой части (или, в этом отношении, слева) двоичного оператора.
Во-первых, списки инициализаторов не являются выражениями, как определено в §5 Стандарта. Аргументы функций, а также бинарных операторов, как правило, должны быть выражениями, и грамматика для выражений, определенных в §5, не включает синтаксис для скобок-init-списков (то есть чистых инициализаторов-списков; обратите внимание, что после имени типа следует с помощью фигурного списка инициализации, такого как 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"}]
является законным), и последний элемент позволяет им в правой части присваивания (но не общие бинарные операторы).
Для бинарных операторов, таких как +
, *
или же <<
следовательно, вы не можете поместить чистый список инициализатора (то есть тот, которому не предшествует имя типа) по обе стороны от них.
Что касается причин этого, проект / дискуссионный документ N2215, составленный Страуструпом и Дос Рейсом от 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] в качестве левых аргументов для большинства операторов - слишком много пустяков, [...]
Другими словами, списки инициализаторов не включены с правой стороны, потому что они не включены с левой стороны, и они не включены с левой стороны, потому что это было бы слишком большой проблемой для анализаторов.,
Интересно, можно ли было упростить проблему, выбрав другой символ вместо фигурных скобок для синтаксиса списка инициализаторов.