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

时间:2017-11-02 11:07:31

标签: c++ c++11 c++14 extern one-definition-rule

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

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

v | print; // prints the vector v

我在此处提供的代码适用于g++clang,即使print是一个没有链接的extern变量,但我想知道我是不是把标准推得太远了。这是针对c ++ 11 / c ++ 14的,我想这是用c inline变量在c ++ 17中解决的?

首先,我的初始代码。目标是允许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;
}

将其移至标题

如果我将其移到标题中,我可以将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';
    }
}

这对我有用。不知何故,即使print没有定义,我也可以v|print。我想这是因为它是空的,因此检查没有价值,因此它永远不需要地址。

编译器是否需要允许我的v|print示例工作?在澄清的情况下,printextern并且没有给出任何定义?

1 个答案:

答案 0 :(得分:7)

  

编译器是否需要允许我的v|print示例工作?在澄清的地方,打印是extern并且没有给出任何定义?

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

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

constexpr print_tag_t print{};

现在,v | print不会使用print(因为左值到右值的转换现在是一个常量表达式),所以甚至不需要定义,所以程序很好-formed。