Clang ++ hace que el vinculador falle en las clases de plantilla (pero funciona con g ++)

Dec 23 2020

Tengo un programa que funciona bien con GCC, pero compilarlo con Clang hace que el enlazador falle.

Creo que mi problema es con las clases de plantilla, así que implementé este pequeño ejemplo.

test.cpp:

#include "Point.h"

int main()
{
    Point<int> p1;
    Point<int> p2{ 3, 6 };
}

Point.h:

#ifndef POINT_H
#define POINT_H

template<typename T>
class Point {
    T x, y;
  public:
    Point();
    Point(T, T);
};

template class Point<int>;
template class Point<float>;

#endif

Point.cpp:

#include "Point.h"

template<typename T>
Point<T>::Point()
    : x{0}, y{0}
{}

template<typename T>
Point<T>::Point(T t_x, T t_y)
    : x{t_x}, y{t_y}
{}

Cuando lo construyo g++ Point.cpp test.cpp, funciona sin problemas.

Pero con Clang, da los siguientes errores:

$ clang++ Point.cpp test.cpp
/usr/bin/ld: /tmp/test-8ab886.o: in function `main':
test.cpp:(.text+0x1a): undefined reference to `Point<int>::Point()'
/usr/bin/ld: test.cpp:(.text+0x2d): undefined reference to `Point<int>::Point(int, int)'
clang-11: error: linker command failed with exit code 1 (use -v to see invocation)

O, usando lld:

$ clang++ -fuse-ld=lld Point.cpp test.cpp
ld.lld: error: undefined symbol: Point<int>::Point()
>>> referenced by test.cpp
>>>               /tmp/test-f95759.o:(main)

ld.lld: error: undefined symbol: Point<int>::Point(int, int)
>>> referenced by test.cpp
>>>               /tmp/test-f95759.o:(main)
clang-11: error: linker command failed with exit code 1 (use -v to see invocation)

Supongo que mis instancias explícitas en Point.hno son lo suficientemente buenas para Clang, pero no puedo imaginar lo que quiere.

Respuestas

5 Jarod42 Dec 23 2020 at 07:17

Su instanciación explícita debe ser donde las definiciones sean visibles, por lo que al final de Points.cpp

Desde class_template # Explicit_instantiation :

Una definición de instanciación explícita fuerza la instanciación de la clase, estructura o unión a la que se refieren. Puede aparecer en el programa en cualquier lugar después de la definición de la plantilla, y para una lista de argumentos determinada, solo se permite que aparezca una vez en todo el programa, no se requiere diagnóstico.