lundi 9 octobre 2023

Explicit template instanciation but still undefined reference (with g++, but not with clang++)

In the code below, I am getting

main.cpp: undefined reference to `(anonymous namespace)::_result<int, double>::type
                                  PPP<int>::operator()<double>(double const&) const'

even though that template is explicitly instanciated. The code hase 3 modules: man.cpp uses PPP<type>, ppp.cpp explicit instanciates PPP<int> and PPP<double>, and aaa.cpp instanciates a method template.

Sorry for the lengthy test case, I couldn't get it shorter, and it appears the result_type template is needed for the fail.

ppp.h

#include <type_traits>

namespace
{
    template<typename T> struct _prio {};
    template<> struct _prio<int>    { using type = std::integral_constant<int,1>; };
    template<> struct _prio<double> { using type = std::integral_constant<int,2>; };

    template<typename A, typename X>
    struct _result
    {
        static constexpr bool cond = _prio<A>::type::value > _prio<X>::type::value;
        using type = typename std::conditional<cond, A, X>::type;
    };
} // ::anon

template<typename T>
struct PPP
{
    template<typename X>
    using result_type = typename _result<T,X>::type;

    T t;
    T operator () (const T&) const;

    // aaa.cpp contains an explicit instanciation for T=int, X=double
    // which is used in main().
    template<typename X>
    result_type<X> operator () (const X&) const;
};

// Instanciated in ppp.cpp
extern template struct PPP<int>;
extern template struct PPP<double>;

ppp.cpp

#include "ppp.h"

template<typename T>
T PPP<T>::operator () (const T& s) const
{
    return s + t;
}

template struct PPP<int>;
template struct PPP<double>;

aaa.cpp

#include "ppp.h"

template<class T>
template <class X>
auto PPP<T>::operator () (const X& x) const -> result_type<X>
{
    return x - (X) t;
}

extern template
auto PPP<int>::operator () (const double&) const -> result_type<double>;

template
auto PPP<int>::operator () (const double&) const -> result_type<double>;

main.cpp

#include "ppp.h"
#include <iostream>

extern template
auto PPP<int>::operator () (const double&) const -> result_type<double>;

int main (void)
{
    PPP<int> pi { 10 };
    std::cout << pi.t << std::endl;
    std::cout << pi (22) << std::endl;

    // Triggers with g++ v11.4, but not with clang++
    // main.cpp: undefined reference to `(anonymous namespace)::_result<int, double>::type
    //                                   PPP<int>::operator()<double>(double const&) const'
    std::cout << pi (33.3) << std::endl;

    return 0;
}

compile

$ <compiler> main.cpp ppp.cpp aaa.cpp -o main.x -Wall

Where <compiler> is one of g++ or clang++. With clang, it compiles and links fine, but with g++ v11.4 the linker complains

/usr/bin/ld: in function `main':
main.cpp: undefined reference to `(anonymous namespace)::_result<int, double>::type
                                  PPP<int>::operator()<double>(double const&) const'
collect2: error: ld returned 1 exit status

When I am adding -save-temps -dumpbase "" to g++ options, and then cat aaa.s | c++filt, the definition of the method can be seen, but it is neither .global nor .weak. And with -O2 the method is no more present. With clang++ it is .weak and thus visible.

So can someone explain what I am missing?

Aucun commentaire:

Enregistrer un commentaire