10

C++ does not instantiate templates with, say T = Hoge&.

A minimal example:

  • 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;
    }
    

I compiled these with: g++ -std=c++11 main.cpp hoge.cpp. But it gives a linker error:

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)

Next, I changed f(h) in main.cpp to f<Hoge &>, and the error disappeared.

Why is f<Hoge &>(Hoge &) not called in the first case? For this case, I can avoid errors by typing f<Hoge &> every time. But, when it comes to overloaded operators, it cannot be done.

Please tell me how to solve this error.

Boann
  • 48,794
  • 16
  • 117
  • 146
naughie
  • 315
  • 2
  • 14

2 Answers2

16

The compiler will try to deduce the simplest template T possible. Here, T=Hoge is fine, so the compiler doesn't try more elaborated forms.

You can clearly state your intent though. Try the following:

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

T will still be deduced as Hoge, but your function f will get a reference. This allows the reader to clearly see that directly in f prototype.

When it comes to template argument deduction, there is a lot of rules occurring under the compiler hood. When I state the compiler deduce the simplest T possible, I'm really cutting corners. Here is a fiable source: cppreference

johan d
  • 2,798
  • 18
  • 26
5

You can instantiate the function with a reference type just the way you did. However, the compiler will not deduce the template argument as a reference type. You can verify that you can instantiate the function template OK by not having the compiler deduce the argument but rather specifying the argument:

f<Hoge&>(h);

If you want to get a reference type deduced you'll need to use a forwarding reference as an argument of your function template:

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

When using a forwarding reference as a template argument and passing an Hoge argument, the argument is deduced according to the value category of argument:

Hoge       h;
Hoge const c;

f(h);       // T becomes Hoge&
f(c);       // T becomes Hoge const&
f(Hoge());  // T becomes Hoge
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 1
    @DietmarKühl I'm sure you mean "will *not* deduce the template argument as a reference type" (unless a forwarding reference is used). – Arne Vogel Jan 09 '18 at 14:10
  • 1
    @M.M Actually, `T` becomes `Hoge` in this case as well. It's the same as in `f(Hoge())`. Note that `T` is the name of the template parameter and not the type of the function parameter (`T &&`). – Arne Vogel Jan 09 '18 at 14:13
  • @ArneVogel OK. [This post](https://stackoverflow.com/a/13726754/1505939) claimed `T&&` deduced for xvalue argument, but maybe it is wrong – M.M Jan 09 '18 at 22:04