Передать строковый литерал в функцию шаблона

Я пытаюсь специализировать функцию шаблона для нескольких типов, один из них — const char*.

template<typename T = const char*>
void f(T&&);

template<>
void f<int>(int&& k) { std::cout << "Now in int fcn (" << k << ")!\n"; }

template<>
void f<const char*>(const char* && k) { std::cout << "Now in cc* fcn (" << k << ")!\n"; }

int main() {
  f<int>(5);
  f("HELLO");

  return 0;
}

Но когда я выполняю f(HELLO), я получаю следующую ошибку:

main.cpp:(.text+0x32): undefined reference to `void f<char const (&) [6]>(char const (&) [6])'

Как заставить его интерпретировать HELLO как константу char*, а не как массив? Если я специализируюсь на массивах, мне нужно по одному для каждого размера массива?

Кроме того, общая функция шаблона перехватывает f(HELLO):

template<typename T>
void f(T&& k) { /* Definition... */ }

Будет ли он создавать одну специализацию для каждого размера массива, который мне нужен, или он каким-то образом преобразует строковый литерал в const char*?


person Skaarjasaurus    schedule 12.11.2020    source источник


Ответы (1)


Строковый литерал не является const char*. Строковый литерал имеет тип const char[N], где N — это количество символов плюс завершающий нуль. Это означает, что при вызове функции T преобразуется в const char[6], который не соответствует ни одной из специализаций, поэтому используется основной шаблон. Поскольку вы не определили основной шаблон, вы получите сообщение об ошибке компоновщика об отсутствии определения.

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

template<std::size_t N> void f(const char (&arr)[N]) { stuff; }

Да, он выделит функцию для массива каждого размера, но это всего лишь небольшое дополнительное время компиляции, вам нужно написать тело только один раз.


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

person NathanOliver    schedule 12.11.2020
comment
Спасибо. Но означает ли это, что я должен создать одну специализацию для каждого используемого размера массива? - person Skaarjasaurus; 13.11.2020
comment
@Skaarjasaurus Зависит от того, что вы хотите сделать. Если вы хотите, чтобы строковые литералы/массивы символов вызывали одну функцию, вы можете добавить перегрузку template<std::size_t N> void f(const char (&arr)[N]) { stuff; }, и это займет массивы любого размера. - person NathanOliver; 13.11.2020