问题 为什么不内联变量?


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

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

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

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

#define Hello (Hello__())

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

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

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

再次清楚地说明,我想要实现的是在标题中定义一个名称空间范围变量,就像内联函数一样,以便使构建过程尽可能简单快速。


Edit2:谢谢 链接 在dyp的评论中。我刚看完提案,这正是我的想法。该提案的现状如何?

引自该提案:

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


2813
2018-05-25 23:25


起源

我看到了你提出的解决方案,但是你要解决的问题是什么? - MSalters
@ xiver77:函数可以内联,因为它们是不可变的。在你的例子中, hello 是可变的,因此它的值必须在使用它的地方之间共享(这样一个地方的变化对其他地方是可见的)。 - Mankarse
此提案可能与您的问题有关: open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4424.pdf - dyp
老实说,黄色引号广场有一个非常好的观点我也很乐意看到解决。在使用静态变量声明类时,这会有很大的凝聚力。不必选择一个.cpp只是为了帮助可怜的小链接器。内联在这里意味着 coalesce definition。 - v.oddou
我全力以赴。我最近尝试使用wxWidgets的一些仅标题部分,但它无法链接,因为 wxString::npos 被提到了。在拥有该定义的库中链接使我的可执行文件大小增加了25MB;我最终把它自己的定义放在了!如果,那会很棒 npos 不需要链接spsecific定义。 - M.M


答案:


C ++ 17有 inline variables,见: N4424


14
2018-06-29 10:19





这是我对你的建议做的一个简短的实验:

文件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

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


2
2018-04-25 06:37



如果您可以在两个不同的编译器上重现结果,为什么会发现结果不可预测? - Chiel
也许这不是描述这种行为的正确方法。但是,我不会使用任何依赖于链接顺序的功能。 - Uri Brecher


变量实际上可以 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
$

0
2018-04-25 07:27





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

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


0
2018-04-26 11:42