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。
我有以下代码:
// 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。