当没有定义时,可以安全地按值传递空变量吗?

Aar*_*aid 8 c++ extern one-definition-rule c++11 c++14

如果我按值传递一个空变量,即使它没有定义,它是否安全且合规?

我在处理代码,重载时遇到了这个问题|,以便打印向量的内容v:

v | print; // prints the vector v
Run Code Online (Sandbox Code Playgroud)

我在这里的代码对我的作品有g++clang,即使printextern没有关联的变量,但我不知道如果我推的标准太远.这是针对c ++ 11/c ++ 14,我想这是用c ++ 17解决的inline变量?

首先,我的初始代码.目标是允许v|print打印矢量等内容.我也有更大的目标,与范围相关,但我会在这里关注这个小例子

struct print_tag_t {};

print_tag_t print;

void operator| (std::vector<int> & x, decltype(print) ) {
    for(auto elem : x) {
        std::cout << elem << '\n';
    }
}

int main() {
    std::vector<int>  v{2,3,5,7};
    v | print;
}
Run Code Online (Sandbox Code Playgroud)

将其移至标题

如果我将其移动到标题中,我可以将operator|重载设置为inline.但那怎么样print?我发现我extern可以避免关于重复符号的链接器错误

// print.hh
struct print_tag_t {};

extern  // extern, as I can't use inline on a variable
print_tag_t print;

inline
void operator| (std::vector<int> & x, decltype(print) ) {
    for(auto elem : x) {
        std::cout << elem << '\n';
    }
}
Run Code Online (Sandbox Code Playgroud)

这适合我.不知何故,即使print没有定义,我也能做到v|print.我想这是因为它是空的,因此检查没有价值,因此它永远不需要地址.

编译器是否需要允许我的v|print示例工作?在哪里澄清,print是否extern已经给出了任何定义?

Bar*_*rry 7

编译器是否需要允许我的v|print示例工作?在哪里澄清,印刷是否extern已经给出了任何定义?

不.你是使用odr print(你正在调用一个不产生常量表达式的左值到右值的转换),这意味着你需要一个定义print.但是,这是错误的错误类别之一,无需诊断.由于所讨论的代码不占用print任何地址,因此编译器可能会发出不需要这样定义的代码,因此链接器也会对此感到高兴.通常,它将Just Work™.

更好的解决方案是简单地更改声明print constexpr:

constexpr print_tag_t print{};
Run Code Online (Sandbox Code Playgroud)

现在,v | print不会使用odr print(因为左值到右值的转换现在将是一个常量表达式),所以甚至不需要定义,因此程序格式正确.