2

This is a simple class to illustrate the problem:

class ClassA
{
public:
    template<typename T>
    void Method1(T&& a) {};
};

Then in my main:

    double a = 0;

    ClassA classA;
    classA.Method1(a);

This compiles fine. But when I move the Method1 implementation to a cpp file, like so...

Header:

class ClassA
{
public:
    template<typename T>
    void Method1(T&& a);
};

Implementation:

template<typename T>
void ClassA::Method1(T&& a) {}

template void ClassA::Method1<double>(double&&);

I receive the error message in Visual Studio 2013

error LNK2019: unresolved external symbol "public: void __thiscall ClassA::Method1(double &)" (??$Method1@AAN@ClassA@@QAEXAAN@Z) referenced in function _wmain

Now if in the main I replace the call to Method1 to classA.Method1(std::move(a))

it compiles fine. Why does the header only version compiles fine without the std::move whereas the version with the implementation requires a std::move to compile?

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
Tony
  • 153
  • 6
  • The template gets instantiated as a member function accepting an lvalue reference, because this is what you pass. See Meyers' lecture [https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers ] on the subject. – Igor R. Aug 14 '15 at 18:30
  • 2
    How come this question was closed twice as a duplicate of *"templates must be in a header"*, when OP's problem concerns explicit instantiation wrt. forwarding-references? I haven't seen a similar question on SO. I can't reopen it for the second time myself. – Piotr Skotnicki Aug 14 '15 at 18:39

2 Answers2

1

In:

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

T&& is a forwarding-reference, rather than an rvalue reference.

Now, the call:

classA.Method1(a);

deduces T to be double&. The explicit instantiation you provide:

template void ClassA::Method1<double>(double&&);

is only for rvalue expressions. This is why it compiles and links when you turn a into an xvalue (a kind of an rvalue) with std::move.

Try the following explicit instantiation:

template void ClassA::Method1<double&>(double&);

Still, you don't cover all possibilities a forwarding reference can take, so better move the implementation of Method1 to a header file with the class definition.

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
0

The arguments you've given to the explicit instantiation are incorrect. The lvalue instantiation would be Method1<double&>(double&);.

Puppy
  • 144,682
  • 38
  • 256
  • 465