В частности, 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);
больше не выводится!
Кланг проклят злой силой из будущих стандартов, есть ли у него ошибка, или проблема сейчас на моем стуле?
Вам нужен дополнительный набор скобок при приведении к void
используя функциональную нотацию приведения, в противном случае скобки считаются частью выражения приведения вместо выражения сгиба. сложить синтаксис выражения сам требует набора скобок.
Все следующие работы без каких-либо предупреждений:
void((std::cout << ... << args));
(void)((std::cout << ... << args));
Или просто позвони ostream
функция-член, чтобы избежать неиспользуемого предупреждения о результате
(std::cout << ... << args).flush();
Как Т.С. упоминает в комментариях ниже, поведение с (void)(std::cout << ... << args);
кажется лягушатником Синтаксис для приведенной нотации указан в 5.4 [expr.cast]
монолитно-выражение:
Унарное выражение
(идентификатор типа) приведенное выражение
Поскольку круглые скобки не являются частью выражения приведения, такое использование не должно вызывать предупреждений, и, что более важно, оно должно приводить к печати аргументов.
Я решил лучше взглянуть на эту ошибку в исходном коде 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 в настоящее время находится в разработке). Я был бы более чем счастлив, если бы это было исправлено, будь то я или кто-то еще. По крайней мере, мои выводы документированы где-то на данный момент.
Выражение сгиба из [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);
}