【问题标题】:Why not inline variables?为什么不是内联变量?
【发布时间】:2015-05-25 23:25:44
【问题描述】:

C++ 中的inline 关键字允许在头文件中定义函数,以便编译器可以实际内联它们或只保留函数的一份副本。这允许通过直接在头文件中定义函数来减少编译单元的数量,其优势通常是编译时间快几倍,执行速度可能更快。

为什么不能将相同的模式应用于命名空间范围的变量,而 C++ 中的函数在将它们视为特殊指针时实际上是命名空间范围的变量?

我能想到的是使用内联函数的静态局部变量。

inline std::string& Hello__() { //Edit: Added the &
    static std::string hello("Hello");
    return hello;
}

#define Hello (Hello__())

编辑:我想澄清我的问题如下。

我使用术语“内联”作为编译器的理解。它允许具有相同名称的相同定义在多个编译单元中,允许在标题中定义。 “内联”的主要优点不是宏函数所具有的性能优势,而是由于减少了编译单元数量而缩短了编译时间。它可能会短几倍。

我确实提出了一个让变量像内联函数一样工作的解决方案。但我仍在寻找更好的方法来做到这一点。

再次明确说明,我想要实现的是在头文件中定义一个命名空间范围的变量,就像一个内联函数,以使构建过程尽可能简单和快速。


Edit2:感谢 dyp 评论中的 link。我刚刚阅读了提案,这正是我的想法。该提案的当前状态如何?

引自提案:

然而,渴望存在 全局唯一对象,无需选择单个翻译 定义它的单位。作为一个实际问题,做出这个选择 通常需要使用非平凡的预处理器宏, 单独编译的库,或两者兼而有之。然而,C++ 的一大优势 是它支持开发headeronly库的能力。在 这种静脉,缺乏定义内联变量姿势的能力 对库设计的重大限制。

【问题讨论】:

  • 我看到了您提出的解决方案,但您要解决的问题是什么?
  • @xiver77:函数可以内联,因为它们是不可变的。在您的示例中,hello 是可变的,因此它的值必须在使用它的地方之间共享(以便一个地方的更改对其他地方可见)。
  • 此提案可能与您的问题有关:open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4424.pdf
  • 老实说,黄色引号方块有一个很好的观点,我也希望看到解决。在使用静态变量声明类时,这将有助于提高凝聚力。不必选择一个 .cpp 来帮助可怜的小链接器。这里的 inline 表示coalesce definition
  • 我完全赞成。我最近尝试使用 wxWidgets 的一些仅标题部分,但由于引用了wxString::npos,它无法链接。在包含该定义的库中进行链接将我的可执行文件大小增加了 25MB;我最终把我自己的定义放了进去!如果npos 不需要链接特定的定义,那就太好了。

标签: c++ inline language-lawyer


【解决方案1】:

C++17 有inline variables,参见:N4424

【讨论】:

  • 值得注意的是,g++ 6.4.0、Ubuntu 16.04 具有标志 -std=c++17,但仍然因错误而失败,可能当时尚未实现。但是,相同的代码适用于较新的 GCC:stackoverflow.com/questions/38043442/…
【解决方案2】:

这是我根据你的建议做了一个简短的实验:

文件 a.cpp:

inline int& get_inline_int() {
    static int my_inline_int = 0;
    return my_inline_int;
}

void set_me(int x) {
    get_inline_int() = x;
}

文件 b.cpp:

#include <iostream>

inline int& get_inline_int() {
    static int my_inline_int = 0;
    return my_inline_int += 2;
}

void show_me() {
    std::cout << get_inline_int() << std::endl;
}

文件 main.cpp:

void set_me(int);
void show_me();

int main() {
    set_me(7);
    show_me();
    set_me(8);
    show_me();
    return 0;
}

我作弊了一点,对同一个函数给出了两种不同的实现。没有内联,链接器会抱怨重复的符号,但我使用内联就可以了。

我不得不承认结果让我吃惊。我用 g++ 和 clang++ 都试过了,得到了类似的结果:

clang++ a.cpp b.cpp main.cpp -o runme

会输出

7
8

clang++ b.cpp a.cpp main.cpp -o runme

会输出

9
10

所以,我认为这是对语言的误用,因为编译结果是不可预测的,而且通常不是你想要的那样。如果委员会能够定义可预测的行为,我会自己使用这些所谓的“内联”变量。

【讨论】:

  • 如果可以在两个不同的编译器上重现结果,为什么会发现结果不可预测?
  • 也许这不是描述这种行为的正确方式。但是我不会使用任何依赖于链接顺序的功能。
【解决方案3】:

变量实际上可以是inlined,但它们不会在全局范围内相同。

zzz.h:

#ifndef ZZZ_H_b6e267bb76401a0cd6502e426a702e41d792a853
#define ZZZ_H_b6e267bb76401a0cd6502e426a702e41d792a853

namespace {
    int omg;
}

static int hello;

// int thisWouldBreakCompilationSoIsCommentedOut;

#endif

xxx.cpp:

#include "zzz.h"

zzz.cpp:

#include "zzz.h"

int main() {}

让我们看看它是否完全编译:

$ g++ xxx.cpp zzz.cpp
$

【讨论】:

    【解决方案4】:

    您的“内联变量”Hello 的行为与全局变量完全一样。唯一的区别是,除了在头文件中声明之外,全局变量还需要在单个编译单元中定义,而Hello 不需要。

    我想,“内联变量”没有语言支持的原因仅仅是因为全局变量无论如何都被认为是邪恶的。您根本不会在现代代码中使用它们。因此,语言不应该增加其语法的复杂性来支持那些无论如何都不会使用的东西。

    【讨论】:

      猜你喜欢
      • 2021-09-04
      • 1970-01-01
      • 2010-12-05
      • 1970-01-01
      • 2010-09-11
      • 2019-02-04
      • 2018-03-23
      • 2013-09-30
      • 1970-01-01
      相关资源
      最近更新 更多