Незначительный вопрос относительно храмовых функций в шаблонном классе - PullRequest
1 голос
/ 19 марта 2009

Я пытаюсь понять синтаксис C ++:

template<class T>
class Foo 
{
   Foo();

   template<class U>
   Foo(const Foo<U>& other);
};

template<class T>
Foo<T>::Foo() { /*normal init*/ }

template<class T>
template<class U>
Foo<T>::Foo(const Foo<U>& other) { /*odd copy constructed Foo*/ }

Итак, я написал такой код, и он прекрасно компилируется в Windows и Linux. Что я не понимаю, так это то, почему конструктор копирования имеет два шаблона, определенных как таковые. По сути, мне пришлось немного истечь, прежде чем я нашел правильный синтаксис, и я хотел бы знать, почему этот конкретный синтаксис правильный, а не что-то вроде template<class T, class U>.

Ответы [ 2 ]

7 голосов
/ 19 марта 2009

Первый шаблон (с параметром T) говорит, что класс настроен с параметром T.

Второй шаблон (с параметром U) говорит, что функция-член шаблонного класса (с параметром T) шаблонизируется с параметром U - то есть конструктором.

На самом деле, здесь у вас есть шаблонный класс, который сгенерирует столько конструктора копирования, сколько типов используется в качестве параметра конструктора.

В конкретном случае конструктора копирования вы не должны делать это, а вместо этого:

template<class T>
class Foo 
{
   Foo();

   Foo(const Foo<T>& other);
};

template<class T>
Foo<T>::Foo() { /*normal init*/ }

template<class T>
Foo<T>::Foo(const Foo<T>& other) { /*odd copy constructed Foo*/ }

Потому что в вашем примере это не конструктор копирования, а конструктор, принимающий тип U в качестве параметра: конструктор преобразования ... который трудно предсказать.

4 голосов
/ 19 марта 2009

Он должен иметь отдельные предложения template для каждого используемого шаблона. Здесь задействованы два шаблона, которые все заслуживают своих (не пустых) предложений шаблонов:

  • Шаблон класса Foo
  • Шаблон конструктора

Рассмотрим этот случай, который не срабатывает из-за неоднозначности того, где параметр U принадлежит

template<typename T>
struct A {
    template<typename U> void f();
};

template<typename T, typename U>
void A<T>::f() { }

Теперь, что случилось с параметром U? Конечно, компилятор может догадаться, что он может принадлежать f, но догадка - это не то, что нравится компилятору :). Существующее правило гласит, что в зависимости от вложенности шаблонов предложения шаблонов отображаются в правильном порядке. Тогда все ясно.

Даже если кто-то придумает правило, как сопоставлять параметры с аргументами задействованных шаблонов (пока я не вижу реальной трудности в этом), это будет противоречиво. Поскольку на данный момент одно предложение шаблона перечисляет все параметры, которые принимает соответствующий шаблон. Очень похоже на список параметров функции. Если бы мы поместили все в одно предложение, эта четкая семантика могла бы быть нарушена - не говоря уже о том, что когда мы снова поместим определение в класс, шаблон вдруг получит свое собственное предложение:

// provides arguments for A's parameters, then for f ones 
// when it's called
A<int> a; 
a.f<bool>();

Это гораздо более естественно, когда у нас есть отдельные предложения шаблона, которые улавливают для каждого свои аргументы. Таким образом, синтаксис для указанного выше неправильного определения:

template<typename T> 
 template<typename U>
void A<T>::f() { }

Теперь также читатель кода сразу видит, что это определение шаблона элемента, а не (потенциально случайно объявленный, но неиспользованный) второй параметр для A.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...