c – 参考文献的条件分配

有人对C标准比我更深入了解请详细说明吗?

这是我的示例程序

#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;
}

在一台机器上它这样做:

# g++ --version && g++ str_test.cpp && ./a.out                  
g++ (Debian 4.7.2-5) 4.7.2
Copyright (C) 2012 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.

0x9851014
0x9851014

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构建了一个消息副本以指向.

根据C标准,哪种行为是正确的?或者它一般是不确定的?

最佳答案
Martin Bonner解释了为什么即使对于字符串的副本,地址也可能相同.

为了解释,为什么消息和toPrint似乎引用了我所期望的相同实例.被误导了,我会引用标准.

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

[expr.cond]/3 Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to convert each of those operands to the type of the other. The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:

  • If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted to the type “lvalue reference to T2“, subject to the constraint that in the conversion the reference must bind directly to an lvalue. (cannot bind lvalue reference of type std::string to a strig literal)
  • If E2 is an xvalue: E1 can be converted to match E2 if E1 can be implicitly converted to the type “rvalue reference to T2“, subject to the constraint that the reference must bind directly. (no xvalues here)
  • If E2 is an rvalue or if neither of the conversions above can be done and at least one of the operands has (possibly cv-qualified) class type:
    • if E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other: E1 can be converted to match E2 if the class of T2 is the same type as, or a base class of, the class of T1, and the cv-qualification of T2 is the same cv-qualification as, or a greater cv-qualification than, the cv-qualification of T1. If the conversion is applied, E1 is changed to a prvalue of type T2 by copy-initializing a temporary of type T2 from E1 and using that temporary as the converted operand. (string literal has no class type)
    • Otherwise (i.e., if E1 or E2 has a nonclass type, or if they both have class types but the underlying classes are not either the same or one a base class of the other): E1 can be converted to match E2 if E1 can be implicitly converted to the type that expression E2 would have if E2 were converted to a prvalue (or the type it has, if E2 is a prvalue). (this applies)

最后一个子弹涵盖了这个案例.字符串文字具有非类型类型,可以转换为匹配std :: string prvalue.

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

4 If the second and third operands are glvalues of the same value category and have the same type (they are not), the result
is of that type and value category and it is a bit-field if the second or the third operand is a bit-field, or if
both are bit-fields.

5 Otherwise, the result is a prvalue. If the second and third operands do not have the same type, and either has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands (13.3.1.2, 13.6). If the overload resolution fails, the program is ill-formed. Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this section.

所以,结果是一个prvalue!这不是左值参考.你怎么从左值得到一个prvalue?

6 Lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the second and third operands. After those conversions, one of the following shall hold:

  • The second and third operands have the same type; the result is of that type. If the operands have
    class type (they do after the conversion), the result is a prvalue temporary of the result type, which is copy-initialized from either
    the second operand or the third operand depending on the value of the first operand.

因此,我们知道结果将从操作数表达式进行复制初始化.即使我们分配了一个引用,并且条件的操作数是对同一类型的左值引用,引用将绑定到临时,从操作数复制.

如果你使用另一个对const std :: string的左值引用作为第三个操作数,那么你只需要分配给左值,而不是临时的prvalue.

转载注明原文:c – 参考文献的条件分配 - 代码日志