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

G++ отвергает, clang++ принимает: foo (x) ( "bar" ) ( "baz" );

Кто-то спросил , что-то компилируется с clang, но не с gcc. Я интуитивно понял, что происходит, и смог помочь человеку, но мне стало интересно - согласно стандарту, какой компилятор был прав? Вот своя версия кода:

#include <iostream>
#include <string>

class foo
{
public:
    foo(const std::string& x):
        name(x)
    { }
    foo& operator()(const std::string& x)
    {
        std::cout << name << ": " << x << std::endl;
        return (*this);
    }
    std::string name;
};

int main()
{
    std::string x = "foo";
    foo(x)("bar")("baz");
    return 0;
}

Это компилируется с помощью clang++, но g++ дает следующую ошибку:

runme.cpp: In function ‘int main()’:
runme.cpp:21:11: error: conflicting declaration ‘foo x’
    foo(x)("bar")("baz");
        ^
runme.cpp:20:17: error: ‘x’ has a previous declaration as ‘std::string x’
    std::string x = "foo";

Если я добавлю пару круглых скобок в строке 21, g++ счастлив:

(foo(x))("bar")("baz");

Другими словами, g++ интерпретирует эту строку как:

foo x ("bar")("baz");

Определяет свою ошибку в g++, но опять же, я хотел спросить стандартных экспертов, какой компилятор ошибся?

PS: gcc-4.8.3, clang-3.5.1

4b9b3361

Ответ 1

Насколько я могу судить об этом, мы рассмотрим в проекте стандартного раздела С++ 6.8 Ambiguity, в котором говорится, что между выражениями выражения и объявлениями может быть неопределенность и говорит:

Существует двусмысленность в грамматике, включающая выражения-выражения и объявления: выражение выражение с функцией-стиль явное преобразование типа (5.2.3), поскольку его самое левое подвыражение может быть неотличимы от декларации, где начинается первый декларатор с. (В этом случае утверждение является объявлением. [Примечание: для устранить неоднозначность, все выражение, возможно, придется изучить определить, является ли это выражением-выражением или декларацией. Эта неоднозначно много примеров. [Пример: предполагая, что T является простой тип-спецификатор (7.1.6),

и дает следующие примеры:

T(a)->m = 7; // expression-statement
T(a)++; // expression-statement
T(a,5)<<c; // expression-statement

T(*d)(int); // declaration
T(e)[5]; // declaration
T(f) = { 1, 2 }; // declaration
T(*g)(double(3)); // declaration

а затем говорит:

Остальные случаи - это объявления. [Пример:

class T {
    // ...
   public:
    T();
    T(int);
    T(int, int);
};
T(a); // declaration
T(*b)(); // declaration
T(c)=7; // declaration
T(d),e,f=3; // declaration
extern int h;
T(g)(h,2); // declaration

-end example] -end note]

Кажется, что этот случай попадает в примеры объявления, в частности, последний пример, похоже, делает случай в OP, поэтому gcc будет правильным.

Соответствующий раздел, упомянутый выше 5.2.3 Явное преобразование типа (функциональная нотация) говорит:

[...] Если указанным типом является тип класса, тип класса должен быть полным. Если выражение list указывает более чем одно значение, тип должен быть классом с соответствующим объявленным конструктором (8.5, 12.1), и выражение T (x1, x2,...) эквивалентно по существу объявлению T t (x1, x2,...); для некоторых придумал временную переменную t, результатом которой является значение t в качестве значения pr.

и 8.3 Значение деклараторов, в котором говорится:

В объявлении T D, где D имеет вид

( D1 ) 

тип содержащегося идентификатора declarator-id такой же, как у содержащий декларатор-id в объявлении

T D1

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

Обновить

Я изначально использовал N337, но если мы посмотрим на N4296, раздел 6.8 был обновлен теперь он включает следующее примечание:

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

что означает, что gcc неверно, так как:

foo x ("bar")("baz");

не может быть допустимым объявлением, я изначально интерпретировал абзац 2, говоря, что если вы начинаете с любого из следующего, то это объявление, которое, возможно, также интерпретируется и разработчиком gcc.

Я должен был более подозрительно относиться к параграфу 2, так как единственная нормативная часть параграфа 2 действительно ничего не говорила в отношении параграфа 1 и, по-видимому, помещает требование на пример, который не является нормативным. Мы можем видеть, что эта формулировка с параграфом 2 теперь является фактически запиской, которая имеет гораздо больше смысла.

Как T.C. ниже, пункт 2 на самом деле никогда не был нормативным, он появился именно так и он связан с изменением, которое зафиксировало его.

Ответ 2

Если мы удалим строку

std::string x = "foo";

то g++ жалуется на:

foo(x)("bar")("baz");

с синтаксической ошибкой:

foo.cc:20:18: error: expected ',' or ';' before '(' token
     foo(x)("bar")("baz");

Я не вижу, как foo (x)("bar")("baz"); может быть допустимым объявлением, и, видимо, g++ тоже не может. Строка foo x("bar")("baz"); отклоняется с той же ошибкой.

"Разрешение неоднозначности", упомянутое в сообщении Shafik, срабатывает только тогда, когда выражение-выражение синтаксически неотличимо от декларации. Однако в этом случае он не является допустимым синтаксисом объявления, поэтому нет двусмысленности, он должен быть выражением-выражением.

g++ не обрабатывает строку как выражение-выражение, так что это ошибка g++.

Это жутко похоже на эту g++ ошибку, недавно обсуждаемую на SO; кажется, что g++, возможно, слишком скоро решит, что строка должна быть объявлением.