何时定义 class' 静态数据成员 (un/-) necesary
When is definition of class' static data member (un/-)necesary
我有一个大项目,正在努力重构它。主要任务是重写记录器。新记录器(据我所知)是 API-compatible 与旧记录器,所以我相信在更改 header 包含目录后,重新编译和重新链接一切都应该工作。但不是。我收到多个 undefined reference to <static_data_member>
类型的错误。我无法粘贴实际代码,但它看起来像这样的示例:
// Foo.h
class Foo {
static const int bar = 0;
int baz; // assigned in c-tor
void updateBaz() { baz = bar; }
// ....
}
static const int bar
未在 Foo.cpp 中定义。它有时由日志宏打印。它曾经工作(使用旧记录器),现在我必须定义它。什么变化可能导致它?
另一个与 boost 声明的变量一起出现的例子:
(...)/blog_adaptor.h:50: error: undefined reference to bbost::serialization::version<CA::CReyzinSignature>::value'
那么:什么时候需要静态成员的定义,什么时候可以省略?
除非变量声明为 inline
(C++17 特性),静态成员变量的定义不是 可选的,就 C++ 标准而言担心的。未能提供定义是未定义的行为。
编译器和链接器可能会根据使它们检查定义是否存在的确切内容而有所不同,但这是未定义行为的本质。
正如 Nicol Bolas 回答的那样,我项目中的代码有未定义的行为,因为静态数据成员已初始化但未定义。总结和扩展:
在以下情况下不需要定义静态数据成员:
- 未使用或仅在丢弃分支(非实例化模板和constexpr-if的丢弃分支)中使用
- 在 C++17 中如果成员是内联的
- clang-tidy 也说 "out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated",所以可能 static constexpr 也不需要它
此外,以下代码显示了为什么我的错误项目之前没有触发 linker 错误。不知道是"not odr-use"还是"Undefined Behavior that doesn't hurt you yet":
#include <boost/serialization/version.hpp>
class Klass {};
//BOOST_CLASS_VERSION(Klass, 3);
// would be expanded to:
namespace boost { namespace serialization {
template<>
struct version<Klass> {
static const int value = 3; // not defined anywhere
};
} }
int foo (int val) { // was used by old logger
return val;
}
int bar (const int &val) { // is used by new logger
return val;
}
int main () {
// return bar(boost::serialization::version<Klass>::value); // link error
return foo(boost::serialization::version<Klass>::value); // works fine
}
因此,如果使用成员但不查询其地址,则不会出现 link 错误。通过引用传递值符合查询地址的条件。
我有一个大项目,正在努力重构它。主要任务是重写记录器。新记录器(据我所知)是 API-compatible 与旧记录器,所以我相信在更改 header 包含目录后,重新编译和重新链接一切都应该工作。但不是。我收到多个 undefined reference to <static_data_member>
类型的错误。我无法粘贴实际代码,但它看起来像这样的示例:
// Foo.h
class Foo {
static const int bar = 0;
int baz; // assigned in c-tor
void updateBaz() { baz = bar; }
// ....
}
static const int bar
未在 Foo.cpp 中定义。它有时由日志宏打印。它曾经工作(使用旧记录器),现在我必须定义它。什么变化可能导致它?
另一个与 boost 声明的变量一起出现的例子:
(...)/blog_adaptor.h:50: error: undefined reference to bbost::serialization::version<CA::CReyzinSignature>::value'
那么:什么时候需要静态成员的定义,什么时候可以省略?
除非变量声明为 inline
(C++17 特性),静态成员变量的定义不是 可选的,就 C++ 标准而言担心的。未能提供定义是未定义的行为。
编译器和链接器可能会根据使它们检查定义是否存在的确切内容而有所不同,但这是未定义行为的本质。
正如 Nicol Bolas 回答的那样,我项目中的代码有未定义的行为,因为静态数据成员已初始化但未定义。总结和扩展: 在以下情况下不需要定义静态数据成员:
- 未使用或仅在丢弃分支(非实例化模板和constexpr-if的丢弃分支)中使用
- 在 C++17 中如果成员是内联的
- clang-tidy 也说 "out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated",所以可能 static constexpr 也不需要它
此外,以下代码显示了为什么我的错误项目之前没有触发 linker 错误。不知道是"not odr-use"还是"Undefined Behavior that doesn't hurt you yet":
#include <boost/serialization/version.hpp>
class Klass {};
//BOOST_CLASS_VERSION(Klass, 3);
// would be expanded to:
namespace boost { namespace serialization {
template<>
struct version<Klass> {
static const int value = 3; // not defined anywhere
};
} }
int foo (int val) { // was used by old logger
return val;
}
int bar (const int &val) { // is used by new logger
return val;
}
int main () {
// return bar(boost::serialization::version<Klass>::value); // link error
return foo(boost::serialization::version<Klass>::value); // works fine
}
因此,如果使用成员但不查询其地址,则不会出现 link 错误。通过引用传递值符合查询地址的条件。