Переслать определяющий класс в пространстве имен? - PullRequest
3 голосов
/ 09 ноября 2011

Следующий фрагмент не компилируется с Visual Studio 2010, но GCC нравится:

namespace Test { 
    class Baz;
    // Adding class Bar; here and removing the class below makes it work
    // with VC++, but it should work like this, shouldn't it?
    void Foo (Baz& b, class Bar& c);
}

namespace Test { 
    class Bar
    {
        // Making this method non-template works
        template <typename T>
        static void Lalala ()
        {
        }
    };
}

int main ()
{
}

Я что-то делаю глупо или это допустимая ошибка компилятора? Я получаю ошибку: error C2888: 'void Bar::Foo(void)' : symbol cannot be defined within namespace 'Test'

Компилируется с GCC 4.5.1: http://ideone.com/7sImY

[Редактировать] Просто чтобы прояснить, я хочу знать, является ли это допустимым C ++ или нет (и если да, то почему нет) - обходные пути для его компиляции хороши, но не являются частью этого вопроса.

Ответы [ 4 ]

2 голосов
/ 09 ноября 2011

Ну, я тоже попробовал это в codepad.org , и он компилируется, но я не уверен, что должен (не настолько, чтобы обладать функциональностью компилятора C ++)!

Обходной путь: также объявите форвард Bar или вам нужно определить Bar перед тем, как сделать Foo.Другими словами, это компилируется в MSVC:

namespace Test 
{ 
    class Baz;
    class Bar;// also forward-declare Bar
    void Foo (Baz& b, class Bar& c);
}

namespace Test 
{ 
    class Bar
    {
        template <typename T>
        static void Foo ()
        {
        }
    };
}

int main(void)
{

    return 0;
}

Обновление: Я думаю, что это уже может быть сообщение об ошибке в Microsoft ... это выглядит довольно близко: http://connect.microsoft.com/VisualStudio/feedback/details/99218/invalid-error-c2888-when-a-class-is-defined-after-it-is-declared

Обходной путь, цитируемый Microsoft:

A stand-alone forward declaration consists of an elaborated type specifier followed by a semicolon.

insert the declaration

class C2888;

before the declaration of foo(C2888o, C2888). 
1 голос
/ 09 ноября 2011

Я думаю, что код правильно сформирован. Но для доказательства этого наверняка потребуется убедиться, что в Стандарте нет ничего, что противоречило бы использованию.

Некоторые соответствующие цитаты из стандарта C ++ 11:

3.3.2 р6:

Точка объявления класса, впервые объявленного в подробном спецификаторе типа , выглядит следующим образом:

  • для подробного спецификатора типа вида идентификатор ключа класса
    • если подробный спецификатор типа используется в decl-specier-seq или параметре-объявлении-параметре элемента функция определена в области имен, идентификатор объявлен как имя-класса в пространстве имен, которое содержит декларацию; в противном случае, за исключением объявления друга, идентификатор объявляется в наименьшая область, не относящаяся к классу и не являющаяся прототипом, которая содержит объявление.

3.4.4 p2:

Если уточненный спецификатор типа не имеет вложенного имени-спецификатора , и если подробный спецификатор типа не появится в объявлении со следующей формой: идентификатор ключа атрибута спецификатора ключа-seq opt ; идентификатор ищется в соответствии с 3.4.1, но игнорирует любые нетипичные имена, которые были объявлены. ... Если подробный спецификатор типа вводится ключ-класс , и этот поиск не находит ранее объявленное имя-типа , или если уточненный-спецификатор типа появляется в объявлении с формой: идентификатор ключа атрибута спецификатора ключа-seq opt ; подробный спецификатор типа - это объявление , которое вводит имя класса , как описано в 3.3.2.

7.1.6 имеет некоторые определения синтаксиса, устанавливающие, что подробный спецификатор типа может синтаксически быть спецификатором типа . 7.1 устанавливает, что спецификатор типа может синтаксически быть decl-спецификатором , который является синтаксической единицей, используемой в качестве типа в функции объявление-параметра (8.3 0,5).

1 голос
/ 09 ноября 2011

Возможно, это ошибка компилятора.

Изменение порядка параметров изменит результат компиляции.

namespace Test { 
void Foo (class Bar& b, class Baz& c) - will compile.
}
0 голосов
/ 09 ноября 2011

Конструкция class Bar ошибочна. Вы случайно не программист на Си, который не использовал typedef struct { /* members */ } Foo?

В любом случае, вам нужно определить как Bar, так и Baz внутри теста:

namespace Test {
    class Bar;
    class Baz;
};

И удалить class, struct, union и enum ключевые слова при объявлении параметров функции.

С этой модификацией он аккуратно компилируется в g ++ 4.6.

...