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*»?
Этот код вызовет ф-цию, помеченную комментарием «специализация второго шаблона»
int *x = new int;
foo(x);
Как сделать вызов «специализиации первого шаблона для int*»?
А если мы хотим отдельно специализировать первый шаблон, и отдельно — другим способом — второй?
У нас не пролог и не хаскелл, где в языке есть требование — паттерны одной функции должны идти подряд, и если кто не всунул специализацию первого шаблона перед объявлением второго, то сам виноват.
У нас не пролог и не хаскелл, где в языке есть требование — паттерны одной функции должны идти подряд, и если кто не всунул специализацию первого шаблона перед объявлением второго, то сам виноват.
Если компилятор обнаружит две специализации одного и тогоже шаблона, то будет ошибка компиляции или линковки
А если компилятор обнаружит две специализации двух разных шаблонов, то ошибки не будет.
Вот смотрите
Если закомментировать специализацию первого шаблона, линкер выругается. Потому что вторая специализация (SHOW(4)) — не относится к первому шаблону. Потому что второй шаблон замаскировал наличие первого, начиная с точки своего объявления.
А если раскомментировать — всё будет так, как задумано. Но определение должно находиться выше объявления второго шаблона, в этом беда.
Вот смотрите
#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.
Перегрузка и специализация. Тонкий момент