Почему делает
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:: класс B’ для именования конструктора на полностью определенное имя
все же изменение friend B<int>::B<int>()
кому: friend B<int>::B()
результаты в
../src/main.cpp:15: ошибка: никакой ‘пустой 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 () недопустимый синтаксис?
Согласно разрешению дефекта CWG 147 (разрешение было включено в C ++ 03), правильный способ назвать нешаблон Конструктор специализации шаблона класса:
B<int>::B();
, а не
B<int>::B<int>();
Если бы последнее было разрешено, возникла бы двусмысленность, когда у вас есть специализация шаблона конструктора для специализации шаблона класса: будет ли второй
быть для шаблона класса или шаблона конструктора? (см. отчет о дефектах, ссылка на который приведена выше, для получения дополнительной информации об этом)
Итак, правильный способ объявить конструктор специализации шаблона класса в качестве друга:
friend B<int>::B();
Comeau 4.3.10.1 и Intel C ++ 11.1 принимают эту форму . Ни Visual C ++ 2008, ни Visual C ++ 2010 не принимают эту форму, но оба принимают (неправильную) форму friend B
(Я отправлю отчет о дефекте в Microsoft Connect ).
gcc не принимает ни одну из форм до версии 4.5. Ошибка 5023 была обнаружена в gcc 3.0.2, но запрошенное решение в отчете об ошибке было неправильной формой. Похоже, что исправление ошибки 9050 также решает эту проблему, и gcc 4.5 принимает правильную форму. Георг Фриче подтвердил это в комментарии к вопросу.
И почему ваша IDE показывает другу B :: B () как недопустимый синтаксис в последнем случае? Ошибка IDE.
Обходной путь, который я нашел в gcc для случая шаблона, если вы не можете обновить, - это переместить реализацию B () в функцию-член void B :: init () и вместо этого предоставить ей дружбу. Готов поспорить, это тоже закроет вашу IDE.
Даже если вы получите это для компиляции, вы столкнетесь с проблемой переполнения стека, как только попытаетесь создать экземпляр B.
Не помогает определение типа? например
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), или если имя, указанное после спецификатора вложенного имени , совпадает с идентификатор или идентификатор простого-шаблона имя-шаблона в последнем компоненте спецификатора вложенного имени , вместо этого используется имя считается конструктором класса C .
Похоже, что имя ( Base
), указанное после описателя вложенного имени ( Base ::
), совпадает с идентификатором в последнем компоненте спецификатора вложенного имени , поэтому этот код действительно называет конструктор.
Но я не могу согласовать это с разделом 12.1 [ class.ctor ]:
Поскольку у конструкторов нет имен, они никогда не обнаруживаются при поиске имени
Да правда? Как этот язык в 3.4.3.1 снова работает?
typedef-name не должен использоваться в качестве class-name в declarator-id для объявление конструктора.
Это кажется довольно ясным, за исключением раздела 12.1, кажется, обсуждает только вводное объявление конструктора, поскольку параграф 1 исключает спецификатор вложенного имени , friend
и с использованием
. Если это применимо к объявлениям друзей, это также, похоже, запрещает friend Base :: B ();
Специальный синтаксис декларатора с использованием необязательной последовательности спецификаторов функций (7.1.2) за которым следует имя класса конструктора, за которым следует список параметров, который используется для объявления или определения конструктора.
Эти спецификаторы функций являются встроенными
, виртуальными
, явными
, а конструкторы не могут быть виртуальными
] в любом случае.
Я думаю, что вы далеко зашли на территорию причуд с шаблонированными конструкторами друзей. Это компилируется и работает нормально на VS2010, но это приводит к переполнению стека, когда конструктор по умолчанию A
вызывает конструктор по умолчанию B
, который затем снова инстанцирует A
.