Member function template of class template can'

2020-04-18 08:02发布

问题:

Edit: This is not a duplicate of the linked question since I am using explicit instantiation and only a specific type of member functions do not link (others do).

The following code compiles but doesn't link and I don't understand why. It is explicitly instantiating the Vector class to limit the number of possible arguments for T and therefore hides the definition of Vector<T> in a .cpp file.

// fwd_decl.hpp
#pragma once
template<typename T>
struct Vector; // Forward declare Vector to be used in other headers

// Vector.hpp
#pragma once
#include "fwd_decl.hpp"

template<typename T>
struct Vector
{
    template<typename U> // To allow for other types than T to be used
    Vector operator+(const Vector<U> & other) const;
    T x;
    T y;

    // more stuff..
};

// Vector.cpp
#include "Vector.hpp"
template<typename T>
template<typename U>
Vector<T> Vector<T>::operator+(const Vector<U> & other) const
{
    return { static_cast<T>(x + other.x), static_cast<T>(y + other.y) };
}
template struct Vector<int>; // Explicitly instantiate Vector<T> with int

// main.cpp
#include "Vector.hpp"
int main()
{
    Vector<int> b = Vector<int>{ 2, 3 } + Vector<int>{ 4, 5 };
}

The error I'm getting is:

1>main.obj : error LNK2001: unresolved external symbol "public: struct Vector<int> __thiscall Vector<int>::operator+<int>(struct Vector<int> const &)const " (??$?HH@?$Vector@H@@QBE?AU0@ABU0@@Z)

I'm compiling with VC++ 17 in VS 15.9.4.

Note that calls to members of Vector<int> that are not function templates do link normally.

回答1:

You should use the explicit instantion of the method template<typename T> template<typename U> Vector<T> Vector<T>::operator+(const Vector<U> & other) const (for all possible pairs of T and U) in addition to the explicit instantion of the Vector<T> class:

template Vector<int> Vector<int>::operator+(const Vector<short> & other) const;

Also you may simply move the definition of the Vector<T>::operator+ method to the header file.

In C++11 the extern template directive was introduced. You may use it in the header file for Vector<T> class (as @StoryTeller suggested in the comments):

extern template struct Vector<int>;

...to prevent the compiler from instantiating the Vector<T> class in every translation unit its specializations are used. Of course the same extern template directives may also be used for all Vector<T>::operator+ specializations explicitly instantiated in the .cpp file.