引用的条件赋值 可以理解我的C++标准吗?请详细说明一下?

引用的条件赋值 可以理解我的C++标准吗?请详细说明一下?,c++,gcc,C++,Gcc,这是我的示例程序 #include <string> #include <iostream> int main(int argc, char* argv[]) { const std::string message("hello world"); std::cout << std::hex << (void*)message.c_str() << std::endl; const std::string&

这是我的示例程序

#include <string>
#include <iostream>

int main(int argc, char* argv[]) {
    const std::string message("hello world");
    std::cout << std::hex << (void*)message.c_str() << std::endl;
    const std::string& toPrint = (argc > 0) ? message : "";
    std::cout << std::hex << (void*)toPrint.c_str() << std::endl;
    return 0;
}
message
toPrint
似乎引用了与我预期相同的实例。但是,在另一台机器上,会发生以下情况:

# g++ --version && g++ str_test.cpp && ./a.out 
g++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

0x7ffeb9ab4ac0
0x7ffeb9ab4ae0
在这里,编译器似乎为
toPrint
构建了一个指向的
message
副本


根据C++标准,什么行为是正确的?或者它通常是未定义的?

您被GLIBCs写时复制字符串共享弄糊涂了。将测试程序更改为:

#include <string>
#include <iostream>

int main(int argc, char* argv[]) {
    const std::string message("hello world");
    std::cout << std::hex << (void*)&message << std::endl;
    const std::string& toPrint = (argc > 0) ? message : "";
    std::cout << std::hex << (void*)&toPrint << std::endl;
    return 0;
}
#包括
#包括
int main(int argc,char*argv[]){
const std::字符串消息(“hello world”);

Martin Bonner解释了为什么即使是字符串的副本,地址也可以相同

为了解释,为什么
消息和toPrint似乎引用了与我预期相同的实例。
被误导了,我将引用标准

让我们首先探讨需要什么转换(我想这不是这里的问题,只是为了完整性)。否则忽略第一个。它指的是
void
类型表达式的情况

[expr.cond]/3否则,如果第二个和第三个操作数具有不同的类型,并且其中一个具有(可能是cv限定的)类类型,或者如果两者都是相同值类别和相同类型(cv限定除外)的glvalues,则尝试将这些操作数中的每一个转换为另一个的类型。确定类型为
T1
的操作数表达式
E1
是否可以转换为与操作数表达式
E2匹配的过程e> 类型
T2
定义如下:

  • 如果
    E2
    是一个左值:
    E1
    可以转换为匹配
    E2
    如果
    E1
    可以隐式转换为类型“levalue reference to
    T2
    ”,但在转换过程中引用必须直接绑定到左值。(无法将类型为
    std::string
    的左值引用绑定到strig文本)
  • 如果
    E2
    是一个xvalue:
    E1
    可以转换为匹配
    E2
    如果
    E1
    可以隐式转换为类型“rvalue reference to
    T2
    ”,但必须受引用必须直接绑定的约束。(此处无xvalue)
  • 如果
    E2
    是一个右值,或者如果上述两种转换都无法完成,并且至少有一个操作数具有(可能是cv限定的)类类型:
    • 如果
      E1
      E2
      具有类别类型,并且基础类别类型相同或一个是另一个的基类:
      E1
      可以转换为匹配
      E2
      ,如果
      T2
      的类别与
      T1
      的类别相同或基类,并且
      T2
      的cv限定为与
      T1
      的cv限定相同或更大的cv限定。如果应用转换,
      E1
      通过从
      E1
      复制初始化类型为
      T2
      的临时变量并将该临时变量用作转换的操作数,将
      E1
      更改为类型为
      T2
      的PR值。(字符串文字没有类类型)
    • 否则(即,如果
      E1
      E2
      具有非类类型,或者如果它们都具有类类型,但基础类既不相同,也不是另一个的基类):
      E1
      可以转换为匹配
      E2
      如果
      E1
      可以隐式转换为表达式
      E2
      E2
      转换为prvalue时将具有的类型(或者如果
      E2
      是prvalue,则其具有的类型)。(此项适用)
最后一个项目符号涵盖了这种情况。字符串文字具有非类类型,可以将其转换为匹配
std::string
prvalue

现在,让我们探讨转换如何影响结果

4如果第二个和第三个操作数是相同值类别的glvalues,并且具有相同的类型(它们不是),则结果 属于该类型和值类别,如果第二个或第三个操作数是位字段,或者如果 两者都是位字段

5否则,结果为prvalue。如果第二个和第三个操作数的类型不同,并且其中一个操作数具有(可能是cv限定的)类类型,则使用重载解析来确定要应用于操作数的转换(如果有)(13.3.1.2,13.6)。如果重载解析失败,则程序格式错误。否则,将应用由此确定的转换,并在本节的其余部分使用转换后的操作数代替原始操作数

所以,结果是prvalue!它不是左值引用。如何从左值中获取prvalue

6对第二个和第三个操作数执行左值到右值(4.1)、数组到指针(4.2)和函数到指针(4.3)的标准转换。在这些转换之后,下列之一应保持不变:

  • 第二个和第三个操作数具有相同的类型;结果为该类型。如果操作数具有 类类型(它们在转换后执行),结果是结果类型的临时值,它是从 第二个操作数或第三个操作数,具体取决于第一个操作数的值
因此,我们知道结果将从操作数表达式进行复制初始化。尽管我们分配了一个引用,并且条件的操作数是对同一类型的左值引用,但该引用将绑定到从操作数复制的临时引用

如果您使用另一个对
const std::string
的左值引用作为第三个操作数,那么您只需将
#include <string>
#include <iostream>

int main(int argc, char* argv[]) {
    const std::string message("hello world");
    std::cout << std::hex << (void*)&message << std::endl;
    const std::string& toPrint = (argc > 0) ? message : "";
    std::cout << std::hex << (void*)&toPrint << std::endl;
    return 0;
}