显式模板实例化,但仍未定义引用(使用 g++,但不使用 clang++)

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

提问人:emacs drives me nuts 提问时间:10/9/2023 最后编辑:emacs drives me nuts 更新时间:10/9/2023 访问量:24

问:

在下面的代码中,我得到

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

即使该模板是显式实例化的。该代码有 3 个模块:使用 、显式实例化 和 和 实例化方法模板。man.cppPPP<type>ppp.cppPPP<int>PPP<double>aaa.cpp

对不起,测试用例冗长,我无法缩短它,而且失败似乎需要模板。result_type

购买力平价

#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>;

购买力.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;
}

编译

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

其中 是 或 之一。使用 clang 时,它可以很好地编译和链接,但在 g++ v11.4 中,链接器会抱怨<compiler>g++clang++

/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

当我添加到 g++ 选项时,然后可以看到方法的定义,但它既不是也不是。并且与该方法不再存在。使用 clang++ 时,它是可见的。-save-temps -dumpbase ""cat aaa.s | c++filt.global.weak-O2.weak

那么有人可以解释我错过了什么吗?

我读了这篇文章,但它似乎没有为上述情况提供解决方案。

C++11 模板 undefined-symbol

评论

1赞 Den-Jason 10/9/2023
我不会为私有模板使用命名空间,而是使用“私有”命名空间。aaa.cpp 和 ppp.cpp 中构建的任何内容都不会与标头中的命名空间位于同一命名空间中。此外,我喜欢遵循“如果你需要显式实例化模板实例来构建一些东西,那么你可能做错了什么”的规则anonymousanonymous
0赞 emacs drives me nuts 10/9/2023
现在,我使用的是显式实例化,因为否则基本上每个位都必须移动到标头中,并且构建时间将飙升。据我了解,它应该仍然有效吗?
0赞 emacs drives me nuts 10/9/2023
另外,如果匿名命名空间不同,那么 clang++ 在链接不同的命名空间时是错误的吗?
0赞 Ted Lyngmo 10/9/2023
@emacsdrivesmenuts “那么 clang++ 在链接不同的命名空间时是错误的吗?” - 匿名命名空间是所有翻译单元中不同的 TU 唯一命名空间。 有 3 个独特的实例化。_result<int, double>
1赞 emacs drives me nuts 10/9/2023
@Den-Jason:谢谢,使用普通命名空间可以工作。

答: 暂无答案