разрешение спецификации шаблона c ++ и перегрузки - PullRequest
10 голосов
/ 09 июня 2011

Я прочитал Почему бы не специализировать шаблоны функций и после эксперимента немного, Я нашел интересную вещь. Здесь идет main.cxx:

// main.cxx
#include <iostream>

// Declarations
/*
template<class T>
void foo(T);

template<>
void foo(int*);

template<class T>
void foo(T*);
*/

// Definition and specification
template<class T>
void foo(T x)
{
    std::cout << "T version." << std::endl;
}

template<>
void foo(int *i)
{
    std::cout << "int* version." << std::endl;
}

template<class T>
void foo(T *x)
{
    std::cout << "T* version" << std::endl;
}

int main(int argc, char** argv)
{
  int *p;
  foo(p);
}

интересная вещь: если я оставлю часть объявления закомментированной, поведение будет таким же, как в статье, то есть T * версия будет использоваться, если определение версии int * идет до ее определения, а также недостатков и стихов. Однако, если раскомментировать блок объявления, будет вызываться только версия int * независимо от того, какой порядок я использую в определениях или объявлениях. Мой вопрос: как это заявление повлияет на резолюцию?

Есть идеи? Я использую g ++ 4.2.2 на x86_64-redhat-linux

РЕДАКТИРОВАТЬ: упростить этот вопрос после того, как увидел ответ AProgrammer

1 Ответ

10 голосов
/ 09 июня 2011

Распределение источника по трем файлам только запутывает дело: предварительная обработка делает одну единицу компиляции, а поведение зависит только от содержимого CU, а не от того, сколько файлов оно было распределено.

Я думаю, что вы удивлены, что в этом случае

#include <iostream>

template<class T> void foo(T); // A
template<> void foo(int*); // 1
template<class T> void foo(T*); // B

template<class T> void foo(T x)
{ std::cout << "T version." << std::endl; }

template<> void foo(int *i) // 2
{ std::cout << "int* version." << std::endl; }

template<class T> void foo(T *x)
{ std::cout << "T* version" << std::endl; }

int main(int argc, char** argv) {
  int *p;
  foo(p);
}

вы получите int* version. Это ожидаемое поведение. Хотя (1) объявляет специализацию template <typename T> void foo(T), (2) не является определением этой специализации. (2) определяет и объявляет специализацию template<class T> void foo(T*);, которая затем вызывается в main(). Это произойдет, если вы дадите три объявления перед тремя определениями, в какие бы вы не поместили объявления и определения. Определение (2) всегда будет видеть объявление template<class T> void foo(T*); и, следовательно, будет его специализацией.

Когда специализация для шаблона функции объявлена ​​или определена, и это может быть специализация для нескольких шаблонов функций (как здесь (2) может быть специализация двух перегрузок A и B, их просто нужно объявить), это специализация "более специализированного". Вы можете увидеть точное определение «более специализированного» в стандартном разделе 17.5.5.2, но довольно легко увидеть, что B лучше соответствует A для (2) и, следовательно, (2) является специализацией (B) , (1) объявляет специализацию (A), потому что когда (1) объявлено, (B) еще не было замечено. Если вы хотите дать определение (1) после того, как (B) было замечено, вам нужно написать

template <> void foo<int*>(int*) // definition for (1)
{ std::cout << "foo<int*>(int*)\n"; }

Вы также можете быть явным при определении (2):

template<> void foo<int>(int *i) // 2 alternate
{ std::cout << "int* version." << std::endl; }

(но очевидно, что (2) и эта альтернативная версия в том же CU даст вам ошибку).

Вы также можете быть явным при вызове функций:

foo(p); // call (2)
foo<int>(p); // call (2)
foo<int*>(p); // call (1)
...