clang 和 gcc 之间静态和 constexpr 的不同编译 + 链接错误

Different compilation + linking errors for static and constexpr between clang and gcc

我有以下代码:

// template_header.hpp
#ifndef TEMPLATE_HEADER_HPP
#define TEMPLATE_HEADER_HPP

namespace template_header
{
    template <int dim1>
    /*static*/ constexpr int dim2 = 0;

    template <>
    /*static*/ constexpr int dim2<2> = 3;
    template <>
    /*static*/ constexpr int dim2<3> = 5;
}

#endif
// lib1.cpp
#include <array>
#include "template_header.hpp"

template <int dim1>
class lib1_class
{
public:
    std::array< double, template_header::dim2<dim1> > ar1 = {0};
};
// lib2.cpp
#include <array>
#include "template_header.hpp"

template <int dim1>
class lib1_class
{
public:
    std::array< double, template_header::dim2<dim1> > ar1 = {0};
};

如果我编译任何 .cpp 文件且 static 未注释,GCC 会给我一个“显式模板特化不能有存储 class”错误。 如果 static 被注释,我可以编译两个 .cpp 文件,然后将它们 link 作为共享库与 g++ lib1.o lib2.o -shared -o shared_lib.so.

一起编译

但是,如果我用 static 编译并用 clang 注释掉,我在编译过程中没有遇到任何问题,但我得到一个“template_header::dim2<2>'" error during linking. If I uncomment static` 的多重定义,然后一切都会编译并且 link很好。

我对此很困惑,首先考虑到 this answer 表明,由于我的 constexpr 发生在命名空间范围内,它们应该自动成为 static 并且因此,即使 static 被注释掉,也不会对 link 用户造成任何问题。 此外,我不明白为什么预先添加 static 会改变 GCC 编译 .cpp 文件的方式,因为它应该是隐式静态的。 对错误的任何解释 + 可能的修复表示赞赏。

编辑:我正在使用 C++14。

因此,在没有 inline 变量的情况下,我能够实现您的目标。基本思想是有一个“后端”struct 来容纳 static 成员,然后 fully specialize struct 供您选择。如果您尝试使用尚未定义的模板参数访问后端成员,此方法的额外好处是会导致编译器错误。

头文件看起来像

#ifndef TEMPLATED_HEADER_HPP
#define TEMPLATED_HEADER_HPP

namespace template_header {
  /**
   * the "primary" template of the backend struct
   *  notice I leave the variable we want undefined!
   *  if you prefer to have a default for all other values of dim1
   *  you would put that here
   */
  template<int dim1>
  struct backend {};

  template<>
  struct backend<2> {
    static constexpr int dim2 = 3;
  };

  template<>
  struct backend<3> {
    static constexpr int dim2 = 5;
  };

  /**
   * Helper constexpr
   *  this is optional, but makes code outside this file more readable
   *  also, I named it in a way for your source files to be unchanged.
   */
  template <int dim1>
  constexpr dim2 = backend<dim1>::dim2;
}

#endif

我在 Compiler Explorer 上有一个这个想法的工作版本,它验证了这两者都适用于 GCC 和 clang。如果您尝试调用 dim2<dim1>,其中 dim1 不等于 2 或 3,则会生成一个编译器错误,抱怨 backend<dim1>::dims not defined。您可以将其他成员添加到 backend 专业化中,注意在 dim1.

的不同值中保持名称相同

总旁注,为什么要设置ar1 = {0};?根据我对 std array reference 的阅读,这只会将数组中的第一个元素设置为 0。