完全专用模板的 constexpr 静态成员在 CLang 上的动态链接失败
Dynamic linking fails on CLang for constexpr static member of fully specialized template
谁能解释一下 1) 为什么下面的代码不能在 CLang 和 2 上工作2) 应该如何重写才能兼容 与 CLang?
using LinkWeight = float;
template <bool WEIGHTED=true>
struct InpLink {
using Weight = LinkWeight; //!< \copydoc LinkWeight
Weight weight; //!< Link weight
// ...
};
template <>
struct InpLink<false> {
using Weight = LinkWeight; //!< \copydoc LinkWeight
constexpr static Weight weight = 1;
};
此代码在 GCC 上运行良好,但在 Linux Ubuntu x64:
上的 CLang 3.8.1 上存在链接错误
undefined reference to `InpLink::weight'
定义后:
template <>
constexpr typename InpLink<false>::Weight InpLink<false>::weight;
编译时错误是:extraneous 'template<>' in declaration of variable 'weight'
定义后:
template <bool WEIGHTED>
constexpr typename InpLink<false>::Weight InpLink<false>::weight;
编译时错误为:
..cluster.hpp:31:60: error: redefinition of 'weight' as different kind of symbol
constexpr typename InpLink<false>::Weight InpLink<false>::weight;
^
..cluster.h:58:27: note: previous definition is here
constexpr static Weight weight = 1;
这看起来像一个 CLang 错误...
注意: 如果我在模板中有 2 个参数,则相同的示例在 CLang 上运行良好,执行部分特化并将静态 constexpr 权重定义为:
template <bool TMP>
constexpr typename InpLink<false, TMP>::Weight InpLink<false, UNSIGNED>::weight;
有
template <bool WEIGHTED=true, bool TMP=true>
struct InpLink {
using Weight = LinkWeight; //!< \copydoc LinkWeight
Weight weight; //!< Link weight
// ...
};
template <bool TMP>
struct InpLink<false, TMP> {
using Weight = LinkWeight; //!< \copydoc LinkWeight
constexpr static Weight weight = 1;
};
显然我不想使用额外的模板参数来克服链接错误。还有其他方法可以解决这个问题吗?
CLang 3.8.1 或我的完整模板专业化有什么问题?
所以,这似乎是 CLang <= 3.8.1 中的一个错误,它限制了 constexpr 在动态库中对静态成员的使用,并阻止了完全专用模板的静态成员的单独定义。
克服它的方法是:
1. 为 CLang 使用 static const 而不是 constexpr。
2. 只定义与模板声明相对应的静态成员,而不是完全特化:
template <bool WEIGHTED=false>
struct InpLink {
using Weight = LinkWeight; //!< \copydoc LinkWeight
//! Link is unweighted
constexpr static bool IS_WEIGHTED = false;
//! \copydoc SimpleLink<Weight>::weight
// ATTENTION: such complicated definition is required to overcome
// the linking error on CLang
#ifdef __clang__
const
#else
constexpr
#endif // __clang__
static Weight weight
#ifndef __clang__
= 1
#endif // !__clang__
;
// ...
};
template <>
struct InpLink<true> {
using Weight = LinkWeight; //!< \copydoc LinkWeight
Weight weight; //!< Link weight
// ...
};
并在 .cpp 中定义静态权重而不是 header 为:
#ifdef __clang__
// Note: Required by CLang for the linking
template <>
const InpLink<false>::Weight InpLink<false>::weight = 1;
#endif // __clang__
PS 对于发布版本,GCC 确实比 CLang 好得多、可靠得多(CLang 仍然有一些独特的调试优势)。
谁能解释一下 1) 为什么下面的代码不能在 CLang 和 2 上工作2) 应该如何重写才能兼容 与 CLang?
using LinkWeight = float;
template <bool WEIGHTED=true>
struct InpLink {
using Weight = LinkWeight; //!< \copydoc LinkWeight
Weight weight; //!< Link weight
// ...
};
template <>
struct InpLink<false> {
using Weight = LinkWeight; //!< \copydoc LinkWeight
constexpr static Weight weight = 1;
};
此代码在 GCC 上运行良好,但在 Linux Ubuntu x64:
上的 CLang 3.8.1 上存在链接错误undefined reference to `InpLink::weight'
定义后:
template <>
constexpr typename InpLink<false>::Weight InpLink<false>::weight;
编译时错误是:extraneous 'template<>' in declaration of variable 'weight'
定义后:
template <bool WEIGHTED>
constexpr typename InpLink<false>::Weight InpLink<false>::weight;
编译时错误为:
..cluster.hpp:31:60: error: redefinition of 'weight' as different kind of symbol
constexpr typename InpLink<false>::Weight InpLink<false>::weight;
^
..cluster.h:58:27: note: previous definition is here
constexpr static Weight weight = 1;
这看起来像一个 CLang 错误...
注意: 如果我在模板中有 2 个参数,则相同的示例在 CLang 上运行良好,执行部分特化并将静态 constexpr 权重定义为:
template <bool TMP>
constexpr typename InpLink<false, TMP>::Weight InpLink<false, UNSIGNED>::weight;
有
template <bool WEIGHTED=true, bool TMP=true>
struct InpLink {
using Weight = LinkWeight; //!< \copydoc LinkWeight
Weight weight; //!< Link weight
// ...
};
template <bool TMP>
struct InpLink<false, TMP> {
using Weight = LinkWeight; //!< \copydoc LinkWeight
constexpr static Weight weight = 1;
};
显然我不想使用额外的模板参数来克服链接错误。还有其他方法可以解决这个问题吗?
CLang 3.8.1 或我的完整模板专业化有什么问题?
所以,这似乎是 CLang <= 3.8.1 中的一个错误,它限制了 constexpr 在动态库中对静态成员的使用,并阻止了完全专用模板的静态成员的单独定义。
克服它的方法是:
1. 为 CLang 使用 static const 而不是 constexpr。
2. 只定义与模板声明相对应的静态成员,而不是完全特化:
template <bool WEIGHTED=false>
struct InpLink {
using Weight = LinkWeight; //!< \copydoc LinkWeight
//! Link is unweighted
constexpr static bool IS_WEIGHTED = false;
//! \copydoc SimpleLink<Weight>::weight
// ATTENTION: such complicated definition is required to overcome
// the linking error on CLang
#ifdef __clang__
const
#else
constexpr
#endif // __clang__
static Weight weight
#ifndef __clang__
= 1
#endif // !__clang__
;
// ...
};
template <>
struct InpLink<true> {
using Weight = LinkWeight; //!< \copydoc LinkWeight
Weight weight; //!< Link weight
// ...
};
并在 .cpp 中定义静态权重而不是 header 为:
#ifdef __clang__
// Note: Required by CLang for the linking
template <>
const InpLink<false>::Weight InpLink<false>::weight = 1;
#endif // __clang__
PS 对于发布版本,GCC 确实比 CLang 好得多、可靠得多(CLang 仍然有一些独特的调试优势)。