内联变量和内联静态变量有什么区别?

15
我对C++17引入的“内联变量”有点困惑。在“内联变量”和“内联静态变量”之间有什么区别?此外,这是否会受到作用域的影响?
inline T var_no_scope;
inline static T static_var_no_scope;

namespace scope {
  inline T var_scope;
  inline static T static_var_scope;
}

任何解释都将不胜感激!

static 具有外部链接性,且“...它在每个翻译单元中具有相同的地址...”来自 http://en.cppreference.com/w/cpp/language/inline - Richard Critten
自C++17起,声明为“inline”的变量可以在多个翻译单元中定义;只要每个翻译单元中的定义相同即可。它具有外部链接。 同时声明为“inline”和“static”的变量将具有内部(或“静态”)链接。与函数完全相同,除了具有可疑的效用(全局变量...“不要在那座山上死去”)。 - viraltaco_
6个回答

8

在命名空间范围:

似乎inline static等价于只有static

对变量使用inline只有在多个翻译单元中定义同一变量时才有效。由于static将变量限制为单个TU,所以不能有超过一个定义。

在类的作用域:

inline只能出现在static变量上。

它具有普通的效果,允许您直接在头文件中初始化变量。可以选择以下方式之一:

struct A
{
    inline static int a = 42;
};

或者:
struct A
{
    static int a;
};

inline int A::a = 42;

函数范围:

inline 不允许使用。


非常好的解释。/\ - balu
相对于“裸露”的静态变量,有很大的内联静态变量增加;请参阅:https://dev59.com/xFoT5IYBdhLWcg3w6isA#47502744 - Dávid Tóth
@DávidTóth我不知怎么错过了您的评论。 您能详细解释一下吗? 所链接的答案似乎没有涉及“inline static”。 - HolyBlackCat
@HolyBlackCat 当然可以!在类中,没有 inline 的简单 static 变量需要有一个外部编译单元(即在仅头文件库的情况下,C++17 之前的代码需要解决问题)。正如你的回答所述,inline static 变量使它们成为可能。我认为这是值得一提的事情。 - Dávid Tóth

7

对我而言,当它是数据成员时就更有趣了。在 C++17 中,您可以将静态数据成员声明为 inline。优点是您不必在 源文件 中为它们分配空间。例如:

class A
{
// Omitted for brevity
static inline int b = 0;
};

所以,源文件中的 int A::b; 可以被删除。

1

关于内联变量以及为什么我们想要使用它们的出色答案可以在这里找到。简而言之,内联变量允许在多个文件中有多个变量定义,这将导致内存中只有一个变量。这允许在头文件中使用constexpr全局变量。

header.h

namespace constants
{
    inline constexpr double P = 1.11;
}

当定义具有不同的值时,行为是未定义的,但是仅由头文件引起的定义乘法时,这不应该是问题。

其他人指出了类中的一个好的应用:

template<typename T>
struct C
{
    static inline constexpr int c = 10;
};

接下来你可以在任何地方引用这个变量,例如: C<int>::c;


1

inline只适用于具有静态存储期的变量。

你的示例中所有变量都具有命名空间作用域,因此如果它们是static,则声明它们为inline没有净效果。

classstructunion内部的变量仅在声明为static时具有静态存储期。如果这些变量要使用inline,则必须将它们声明为static


2
将它们声明为静态的会给它们内部链接(对于非数据成员),或多或少地破坏了声明它们为内联的目的。 - Khoyo

0

inline static variable可以在类定义中定义,并且可以指定初始化程序。它不需要一个类外定义:

struct X
{
    inline static int n = 1;
};

内联变量消除了将C++代码打包为仅头文件库的主要障碍。

如果您需要声明在编译单元之间共享的全局变量,请在头文件中将它们声明为内联变量

这是否会受到范围的影响?

在命名空间作用域声明的以下任何名称都具有外部链接,并且还包括在多个源文件中声明的没有命名空间的名称必须是内联的

请参见示例

链接提供了关于内联变量的有价值信息。


0

C++参考定义:https://en.cppreference.com/w/cpp/language/inline引用

内联变量被定义为内联静态关键字。因此,内联静态vartype yourvariablename //是一个内联变量。

有趣的是,关于内联函数,“如果一个完全在类/结构/联合定义内部定义的函数,无论它是成员函数还是非成员友元函数,都会自动成为内联函数,如果它附加到全局模块”(C++20)

从个人经验来看,这就是为什么内联静态是一个非常重要的事情:

  1. 如果你只需要在C++文件中初始化静态变量,那么你可以删除C++文件并使用内联静态初始化(但你的代码将需要C++17及以上版本)

  2. 在C++ 17之前的类名模板中初始化静态变量只是过于冗长。你必须做一些像这样的事情:

template class classname { static yourtype yourstaticvariable = yourvalue; // and you'll have to initialize it
};

template yourtype classname::yourstaticvariable = yourvalue;

或者使用内联静态方法,你可以这样做:

template <class T>
class classname
{
    static yourtype yourstaticvariable = yourvalue; // initialized
}
  1. 如果您使用内联静态初始化常见事物,则可以大量利用模板。例如,单例就是一个很好的例子,其中内联静态变量非常适合跟踪实例,因此可以在一个文件中完成,而在初始化静态变量之前则会很麻烦。当您拥有多个变量模板时,这一点变得更加明显。
  2. 如果您利用内联静态来处理单个C++头文件,则需要较少的C++包含文件和库项目。这可以大大减少大型项目中的文件数量。

但是内联静态也有一些注意事项:

  1. 您需要C++ 17及以上版本
  2. 如果您将初始化放在.h文件中(如果您使用内联静态进行初始化,则可能希望这样做),则必须注意问题。尽管这不一定是内联静态问题,但决定潜在地将所有内容移动到标头可能会导致问题,例如名称空间冲突、重用宏或其他问题,如果您在包含顺序上不小心,则可能会出现这些问题。

但总体而言,这是一个非常棒的功能。如果有疑问,请使用内联静态进行初始化,并打破我需要一个c++文件来初始化静态变量模式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接