Pull to refresh

Comments 19

То есть в примере выше (2) является специализацией не (3) а (1), поэтому для перегрузки будет выбран более подходящий базовый шаблон (3). Во втором же случае (6) является специализацией (5), которая в свою очередь является лучшей кандидатурой, чем (4).

Вот тут-то секрета никакого нет. подобное поведение подобно ситуации
fun{
 x=0;
double x;
}

Причина — однопроходность компилятора. А вот то, что * — это спецификатор, вот про здесь я забыл, видать надо в коде писать не int* x, а int *x
Внимание, вопрос.
template<class T> void foo(T ) {...}  // первый базовый шаблон
template<class T> void foo(T*) {...} // второй базовый шаблон

template<> void foo(int*) {...} // специализация второго шаблона

// как сделать специализацию первого шаблона для int* ?

Скорее всего придется функции разнести в разные заголовочные файлы, а специализации делать с в соответсвующих cpp файлах.
Специализация
template<> void foo(int*) { ... }

одновременно является и специализацией первого шаблона с T=int*, и второго с T=int

ИМХО, вопрос не имеет смысла.
Чтобы имело смысл делать две разные специализации, надо иметь возможность их по-разному вызвать.
Этот код вызовет ф-цию, помеченную комментарием «специализация второго шаблона»
int *x = new int;
foo(x);


Как сделать вызов «специализиации первого шаблона для int*»?
Спасибо. Настолько привык, что компилятор сам выводит тип, что позабыл о возможности ручного указания.
А если мы хотим отдельно специализировать первый шаблон, и отдельно — другим способом — второй?

У нас не пролог и не хаскелл, где в языке есть требование — паттерны одной функции должны идти подряд, и если кто не всунул специализацию первого шаблона перед объявлением второго, то сам виноват.
Если компилятор обнаружит две специализации одного и тогоже шаблона, то будет ошибка компиляции или линковки
А если компилятор обнаружит две специализации двух разных шаблонов, то ошибки не будет.

Вот смотрите
#include <cstdio>

#define SHOW(i) printf("%d : %s \n", i, __PRETTY_FUNCTION__)

template<class T> void foo(T) { SHOW(1); } // это - главное определение первого шаблона
template<> void foo(char*); // объявляем

auto fi = foo<int*>;
auto fc = foo<char*>;
auto fs = foo<short*>;

//template<> void foo(char*) { SHOW(2); } // это определение специализации первого шаблона


template<class T> void foo(T*) { SHOW(3); } // это - главное определение второго шаблона
template<> void foo(char*) { SHOW(4); } // это специализация ТОЛЬКО второго шаблона
template<> void foo(int*) { SHOW(5); }

template<class T> void bar(void(*p)(T*))
{
	p(0);
}

int main()
{
	fi(0); // 1
	fc(0); // 2
	fs(0); // 1

	bar<int>(foo); // 5
	bar<char>(foo); // 4
	bar<short>(foo); // 3
}

Если закомментировать специализацию первого шаблона, линкер выругается. Потому что вторая специализация (SHOW(4)) — не относится к первому шаблону. Потому что второй шаблон замаскировал наличие первого, начиная с точки своего объявления.
А если раскомментировать — всё будет так, как задумано. Но определение должно находиться выше объявления второго шаблона, в этом беда.
Потому что второй шаблон замаскировал наличие первого, начиная с точки своего объявления.

Он не замаскировал, он приобрел больший приоритет, как лучше соответствующий.

С шаблонами вообще интересно. В приведенном коде поменям:

template<class T> void foo(T) { SHOW(1); } // это - главное определение первого шаблона
auto fc = foo<char*>;
template<> void foo(char*); // объявляем

auto fi = foo<int*>;
auto fs = foo<short*>;

Получим ошибку компиляцию.

Весь прикол в том, что шаблон словно кот Шредингенра, вроде он есть, а вроде его и нет. Однозначно он начинает существовать только после инстацирования.

Благодаря этому и появляется возможность специализировать и даже частично специализировать или при специализации добавлять новые шаблонные параметры. Ну и т.п.
Резюмируя: специализации шаблонов — это редкостный тёмный лес, и проще туда попросту не соваться, чем соваться, огребать и разгребать.
Правильный ответ на ваш вопрос — не надо ) Не надо писать специализацию для первого шаблона. Если вы хотите написать что-то специфическое для int*, напишите
void foo(int*){}

Общее правило — не пишите специализацию, если можно решить проблему перегрузкой.
UFO just landed and posted this here
Те кто ответил 3 и 6 могут сегодня потратить на торчание на хабре на полчаса больше рабочего времени чем обычно.

Что-то я запутался, может, наоборот — «меньше»? Ведь, не нужно читать статью до конца. Или — время не потраченное на эту статью — можно потратить на какую-то другую? Но тогда, время «торчания» на хабре не изменится? оО
Скорее всего предполагалось, что 30 минут будет проведено за книжками\доками в поисках причины такого поведения.
Имелось в виду, что раз все знаешь, то рабочий процесс не пострадает от того, что на него будет потрачено на полчаса меньше времени, легко наверстаешь, а если нет, то иди работай лучше, а то не успеешь )
Наверное, неясно выразил свою мысль.
Кстати, можете добавить более развёрнутые размышления на эту тему от Sutter-а: Why Not Specialize Function Templates?. Там хорошо разжёвано. Например, указано, что в случае (3) — мы имеем перегрузку функции (second base template, overloads (1)) и что явные специализации не перегружают (Specializations don't overload), отсюда и всё вытекает.
Sign up to leave a comment.

Articles