Clang и двоичные выражения сгиба — проклятие пустого пакета параметров

В частности, Clang 3.6.0, который в настоящее время размещается в Coliru.

Все эти фрагменты вызваны из:

int main() {
foo();
std::cout << "\n----\n";
foo(1, 2, 3);
}

Следующий код:

template <class... Args>
void foo(Args... args) {
std::cout << ... << args;
}

Вызывает следующую ошибку компиляции:

main.cpp:7:17: error: expected ';' after expression
std::cout << ... << args;
^
;
main.cpp:7:15: error: expected expression
std::cout << ... << args;
^

Поэтому я попытался заключить в скобки выражение:

(std::cout << ... << args);

Это работает, но вызывает предупреждение:

main.cpp:7:6: warning: expression result unused [-Wunused-value]
(std::cout << ... << args);
^~~~~~~~~
main.cpp:11:5: note: in instantiation of function template specialization 'foo<>' requested here
foo();
^

Поэтому я попытался отбросить значение выражения с помощью приведения стиля функции к void :

void(std::cout << ... << args);

Но :

main.cpp:7:20: error: expected ')'
void(std::cout << ... << args);
^
main.cpp:7:9: note: to match this '('
void(std::cout << ... << args);
^

Я попробовал static_cast тоже для того же результата.

Поэтому я попробовал с C-cast вместо этого:

(void)(std::cout << ... << args);

Но потом :

main.cpp:6:18: warning: unused parameter 'args' [-Wunused-parameter]
void foo(Args... args) {
^

… и мой вывод только ---- : foo(1, 2, 3); больше не выводится!

Кланг проклят злой силой из будущих стандартов, есть ли у него ошибка, или проблема сейчас на моем стуле?

11

Решение

Вам нужен дополнительный набор скобок при приведении к void используя функциональную нотацию приведения, в противном случае скобки считаются частью выражения приведения вместо выражения сгиба. сложить синтаксис выражения сам требует набора скобок.

Все следующие работы без каких-либо предупреждений:

void((std::cout << ... << args));
(void)((std::cout << ... << args));

Или просто позвони ostream функция-член, чтобы избежать неиспользуемого предупреждения о результате

(std::cout << ... << args).flush();

Как Т.С. упоминает в комментариях ниже, поведение с (void)(std::cout << ... << args); кажется лягушатником Синтаксис для приведенной нотации указан в 5.4 [expr.cast]

монолитно-выражение:
  Унарное выражение
  (идентификатор типа) приведенное выражение

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

10

Другие решения

Я решил лучше взглянуть на эту ошибку в исходном коде Clang. Вот оскорбительный раздел кода. Этот случай происходит, когда он только что закончил анализ (<type>) и теперь анализирует следующее выражение в скобках:

} else if (isTypeCast) {
// Parse the expression-list.
InMessageExpressionRAIIObject InMessage(*this, false);

ExprVector ArgExprs;
CommaLocsTy CommaLocs;

if (!ParseSimpleExpressionList(ArgExprs, CommaLocs)) {
// FIXME: If we ever support comma expressions as operands to
// fold-expressions, we'll need to allow multiple ArgExprs here.
if (ArgExprs.size() == 1 && isFoldOperator(Tok.getKind()) &&
NextToken().is(tok::ellipsis))
return ParseFoldExpression(Result, T);

ExprType = SimpleExpr;
Result = Actions.ActOnParenListExpr(OpenLoc, Tok.getLocation(),
ArgExprs);
}
}

// The beginning of ParseFoldExpression(LHS, T):
if (LHS.isInvalid()) {
T.skipToEnd();
return true;
}

Конкретная часть кода, ответственная за эту ошибку, находится здесь:

return ParseFoldExpression(Result, T);

Оказывается, что Result никогда не отделен от своего начального true значение. Я считаю, что это должно быть установлено ArgExprs.front(), который сейчас держит std::cout,

Теперь вы также заметите FIXME. Хотя, в частности, это не имеет отношения к этой проблеме, возможно, стоит исправить ее вместе с этим.

Будучи моим первым исправлением Clang, у меня еще есть несколько вещей, которые нужно сделать перед отправкой изменений (для справки, Clang 4.0 в настоящее время находится в разработке). Я был бы более чем счастлив, если бы это было исправлено, будь то я или кто-то еще. По крайней мере, мои выводы документированы где-то на данный момент.

3

Выражение сгиба из [expr.prim.fold]:

Выражение сгиба выполняет сгиб пакета параметров шаблона (14.5.3) над бинарным оператором.
    складчато-выражение:
        ( монолитно-выражение складчато-оператор … )
        (… складчато-оператор монолитно-выражение )
        ( монолитно-выражение складчато-операторскладчато-оператор монолитно-выражение )

Обратите внимание, что во всех случаях круглые скобки являются частью грамматики. Итак, ваш первоначальный пример синтаксически некорректен и должен быть:

template <class... Args>
void foo(Args... args) {
(std::cout << ... << args);
}

Это тогда даст вам предупреждение в случае пустой пачки, так как двоичная складка уменьшается до std::cout; Чтобы избавиться от этого предупреждения, вы можете пойти обычным путем void — только то, что внутренний набор скобок является частью грамматики, так что вам нужно два:

void((std::cout << ... << args));

Или вы могли бы просто добавить дополнительный endl или т.п:

(std::cout << ... << args) << std::endl;

Или верните результат:

template <class... Args>
std::ostream& foo(Args... args) {
return (std::cout << ... << args);
}
2
По вопросам рекламы [email protected]