Как подружиться с конструктором шаблонного класса? - PullRequest
6 голосов
/ 13 мая 2010

Почему

class A;
template<typename T> class B
{
private: 
    A* a;

public:  
    B();
};


class A : public B<int>
{
private:    
    friend B<int>::B<int>();
    int x;
};


template<typename T>
B<T>::B()
{
    a = new A;
    a->x = 5;
}

int main() { return 0; }

результат в

.. / src / main.cpp: 15: ошибка: неверное использование конструктора в качестве шаблона
../src/main.cpp:15: примечание: используйте «B :: B» вместо «B :: class B», чтобы назвать конструктор квалифицированным именем

пока меняется friend B<int>::B<int>() на friend B<int>::B(), в результате

.. / src / main.cpp: 15: ошибка: нет функции-члена void B :: B (), объявленной в классе «B»

при полном удалении шаблона

class A;
class B
{
private:
    A* a;

public:
    B();
};


class A : public B
{
private:
    friend B::B();
    int x;
};


B::B()
{
    a = new A;
    a->x = 5;
}

int main() { return 0; }

компилируется и выполняется просто отлично - несмотря на то, что моя IDE говорит, что у друга B :: B () неверный синтаксис?

Ответы [ 4 ]

5 голосов
/ 13 мая 2010

В соответствии с разрешением Дефект CWG 147 (разрешение было включено в C ++ 03), правильный способ назвать нетабличный конструктор специализации шаблона класса:

B<int>::B();

а не

B<int>::B<int>();

Если бы последние были разрешены, возникает двусмысленность, когда у вас есть специализация шаблона конструктора для специализации шаблона класса: будет ли второй <int> для шаблона класса или шаблона конструктора? (см. отчет о дефектах, связанный выше, для получения дополнительной информации об этом)

Итак, правильный способ объявления конструктора шаблона класса как друга:

friend B<int>::B();

Comeau 4.3.10.1 и Intel C ++ 11.1 оба принимают эту форму. Ни Visual C ++ 2008, ни Visual C ++ 2010 не принимают эту форму, но обе принимают (неправильную) форму friend B<int>::B<int>(); (я отправлю отчет о дефектах в Microsoft Connect).

gcc не принимает ни одну из форм до версии 4.5. Ошибка 5023 была сообщена для gcc 3.0.2, но запрошенное разрешение в отчете об ошибке было неверной формой. Похоже, что разрешение ошибка 9050 также решает эту проблему, и gcc 4.5 принимает правильную форму. Георг Фрицше подтвердил это в комментарии к вопросу.

1 голос
/ 13 мая 2010

Не помогает ли typedef? например,

class A : public B<int>
{
    typedef B<int> Base;   
    friend Base::Base();
    int x;
};

РЕДАКТИРОВАТЬ: Проект окончательного комитета для C ++ 0x включает следующий язык в разделе 3.4.3.1 [ class.qual ]:

В поиске, в котором конструктор является приемлемым результатом поиска, а спецификатор вложенного имени назначает класс C : если имя, указанное после вложенного спецификатор имени при поиске в C является именем введенного класса C (пункт 9), или если имя указано после вложенного -name-спецификатор совпадает с идентификатором или simple-template-id * template-name в последнем компоненте спецификатор вложенного имени , вместо этого имя считается именем конструктора класса C .

Похоже, имя (Base), указанное после вложенного имени-спецификатора (Base::), совпадает с идентификатором в последнем компоненте nested-name-спецификатор , поэтому этот код называет конструктор.

Но я не могу согласовать это с разделом 12.1 [ class.ctor ]:

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

О, правда? Как этот язык в 3.4.3.1 снова работает?

A typedef-name не должно использоваться в качестве имя-класса в идентификатор объявления для объявления конструктора.

Это кажется довольно ясным, за исключением того, что раздел 12.1, по-видимому, обсуждает только вводное объявление конструктора, поскольку параграф 1 исключает спецификатор вложенного имени , friend и using. Если это действительно относится к объявлениям о друзьях, оно также запрещает friend Base::B();

Специальный синтаксис объявления, использующий необязательную последовательность спецификаторов функций (7.1.2), за которыми следует имя класса конструктора, за которым следует список параметров, используется для объявления или определения конструктора.

Эти спецификаторы функций являются inline, virtual, explicit, а конструкторы не могут быть virtual в любом случае.

1 голос
/ 13 мая 2010

А причина, по которой ваша IDE показывает друга B :: B () как недопустимый синтаксис в последнем случае? Ошибка IDE.

Обходной путь, который я нашел в gcc для случая шаблона, если вы не можете выполнить обновление, - это переместить реализацию B () в функцию-член void B :: init () и вместо этого предоставить дружбу этому. Готов поспорить, это тоже закроет вашу IDE.

Даже если вы получите это для компиляции, вы столкнетесь с проблемой переполнения стека, как только попытаетесь создать экземпляр B.

0 голосов
/ 13 мая 2010

Полагаю, вы попадаете на причудливую территорию с друзьями-конструкторами. Это компилирует и прекрасно работает на VS2010, но создает переполнение стека, когда конструктор по умолчанию A вызывает конструктор по умолчанию B, который затем снова создает экземпляр A.

...