Как мы можем создавать шаблонные функции со ссылочным типом?

С++ не создает шаблоны, скажем T = Hoge&.

Минимальный пример:

  • hoge.h:

    #include<cstdio>
    class Hoge
    {
      public:
        Hoge()
          : hoge(0)
        {
        }
        ~Hoge()
        {
        }
    
        int hoge;
        void print() { printf("%d\n", hoge); }
    };
    
    template<typename T>
    void f(T a);
    
  • hoge.cpp:

    #include "hoge.h"
    template<typename T>
    void f(T a)
    {
      a.print();
    }
    
    template void f<Hoge &>(Hoge &a);
    
  • main.cpp:

    #include "hoge.h"
    int main(void)
    {
      Hoge h;
      f(h);
      return 0;
    }
    

Я скомпилировал их с помощью g++ -std=c++11 main.cpp hoge.cpp. Но это дает ошибку компоновщика:

Undefined symbols for architecture x86_64:
  "void f<Hoge>(Hoge)", referenced from:
      _main in aa-e35088.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Затем я изменил f(h) в main.cpp на f<Hoge &>, и ошибка исчезла.

Почему f<Hoge &>(Hoge &) не вызывается в первом случае? В этом случае я могу избежать ошибок, набирая f<Hoge &> каждый раз. Но, когда дело доходит до перегруженных операторов, это не может быть сделано.

Скажите, пожалуйста, как решить эту ошибку.

Ответ 1

Компилятор попытается вывести самый простой шаблон T. Здесь T=Hoge отлично, поэтому компилятор не пытается обработать более сложные формы.

Вы можете четко указать свои намерения. Попробуйте следующее:

template<typename T>
void f(T& a);

T по-прежнему будет выводиться как Hoge, но ваша функция f получит ссылку. Это позволяет читателю ясно видеть это непосредственно в прототипе f.

Когда дело доходит до вычитания аргумента шаблона, существует множество правил, возникающих под капотом компилятора. Когда я заявляю, что компилятор выводит простейший T, я действительно режу углы. Вот жизнеспособный источник: cppreference

Ответ 2

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

f<Hoge&>(h);

Если вы хотите получить ссылочный тип, вы должны использовать ссылку пересылки в качестве аргумента своего шаблона функции:

template <typename T>
void f(T&& a);

При использовании ссылки пересылки в качестве аргумента шаблона и передачи аргумента Hoge аргумент выводится в соответствии с категорией значений аргумента:

Hoge       h;
Hoge const c;

f(h);       // T becomes Hoge&
f(c);       // T becomes Hoge const&
f(Hoge());  // T becomes Hoge