Зачем нам нужен шаблонперед реализацией всех шаблонных методов класса - PullRequest
0 голосов
/ 03 декабря 2018

Если у нас есть стандартный класс:

class Foo {
  public:
    int fooVar = 10;
    int getFooVar();
}

Реализация для getFooVar() будет:

   int Foo::getFooVar() {
       return fooVar;
   }

Но в шаблонном классе:

template <class T>
class Bar {
  public:
    int barVar = 10;
    int getBarVar();
}

Реализация для getBarVar() должна быть:

template <class T>
int Bar<T>::getBarVar(){
   return barVar();
}

Почему у нас должна быть строка template <class T> перед реализацией функции getBarVar и Bar<T>:: (в отличие от просто Bar: :), учитывая тот факт, что функция не использует шаблонные переменные?

Ответы [ 5 ]

0 голосов
/ 04 декабря 2018

Рассмотрим объявление шаблона функции

tempalte <typename T> 
void foo();

Теперь определение

void foo() { std::cout << "Hello World"; }

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

#include <iostream>

template <typename T>
void foo();

void foo() { std::cout << "overload\n"; }

template <typename T>
void foo() { std::cout << "specialization\n"; }

int main() {
    foo();
    foo<int>();
}

Отпечатки:

overload
specialization

Краткий ответ на ваш вопрос таков: каковы правила, хотя, если бы вы могли опустить template <typename T> из определенияшаблон, другой способ потребуется для определения перегрузки.

0 голосов
/ 03 декабря 2018

Любой ответ на этот вопрос сводится к «потому что так говорится в стандарте».Однако вместо того, чтобы читать стандартное, давайте рассмотрим, что еще запрещено (потому что ошибки помогают нам понять, чего ожидает язык).Случай «одного шаблона» довольно быстро исчерпан, поэтому давайте рассмотрим следующее:

template<class T>
class A
{
    template<class X>
    void foo(X);
};

Может быть, мы можем использовать один аргумент шаблона для обоих?

template<class U>
void A<U>::foo(U u)
{
    return;
}
error: out-of-line definition of 'foo' does not match any declaration in 'A<T>'

Нет, мы не можем.Ну, может, так?

template<class U>
void A<U>::foo<U>(U u)
{
    return;
}
error: cannot specialize a member of an unspecialized template

Нет.И это?

template<class U, class V>
void A<U>::foo(V u)
{
    return;
}
error: too many template parameters in template redeclaration

Как насчет использования по умолчанию для эмуляции соответствия?

template<class U>
template<class V = U>
void A<U>::foo(V u)
{
    return;
}
error: cannot add a default template argument to the definition of a member of a class template

Понятнокомпилятор беспокоится о соответствии объявления 1031 *.Это связано с тем, что компилятор сопоставляет определения шаблонов не с конкретными вызовами (как можно было бы использовать для функционального языка), а с объявлением шаблона.(Код пока здесь ).

Итак, на базовом уровне ответ «потому что определение шаблона должно соответствовать объявлению шаблона».Это все еще оставляет открытым вопрос "почему мы не можем просто пропустить параметры шаблона класса тогда?"(насколько я могу судить, не может быть никакой двусмысленности для шаблона, поэтому повторение параметров шаблона не помогает), хотя ...

0 голосов
/ 03 декабря 2018

Вам это нужно, потому что Bar - это не класс, это шаблон .Bar<T> это класс.

0 голосов
/ 03 декабря 2018

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

Но давайте теперь предположим, что он вам не нужен, в конце концов, вы указали это, и я добавил еще один аргумент шаблона:

template<typename T1, typename T2>
class Bar
{
    void something();
};

Почему:

template<typename T1, typename T2>
void Bar<T1, T2>::something(){}

А не:

void Bar::something(){}

Что бы произошло, если бы вы хотели специализировать свою реализацию для одного типа T1, а не для другогоодин?Вам нужно будет добавить эту информацию.И именно здесь вступает в действие это объявление template, и поэтому оно также необходимо для общей реализации (IMHO).

template<typename T>
void Bar<T, int>::something(){}
0 голосов
/ 03 декабря 2018

Когда вы создаете экземпляр класса, компилятор проверяет, есть ли реализации.Но в то время, когда вы пишете код, конечный тип (то есть экземплярный тип) неизвестен.

Следовательно, компилятор создает для вас определения, и если компилятор должен что-то создавать, он должен быть шаблонизирован.

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