【问题标题】:Why can const char* const & = "hello" compile?为什么 const char* const & = "hello" 可以编译?
【发布时间】:2019-02-01 14:21:37
【问题描述】:

我正在阅读书中的代码 sn-p 并发现:

const char* const & a = "hello"; //can compile 
const char*& a = "hello"; //cannot

我所知道的是,在初始化引用时,数组到指针的转换不会发生。

const char* const &,一个const pointer的引用,指针指向const char

const char*&,对pointer的引用,指针指向 const char.

那为什么要多加一个const,表示指针是const,才允许编译呢?

【问题讨论】:

  • “初始化引用时,不会发生数组到指针的转换”。这并不完全正确。初始化对数组的引用 时,不会发生数组到指针的转换。没有这样的规则来初始化对指针的引用。

标签: c++ reference constants language-lawyer


【解决方案1】:

它本质上是遵循这个公式

T const & a = something_convertible_to_T;

其中 T 是 const char*。在第一种情况下,可以实现一个临时指针,分配文字的地址,然后将其自身绑定到引用。在第二种情况下,由于左值引用不是 const,它不会发生。更多相同的另一个例子

const char* && a = "hello"; // rvalue ref makes a no into a yes.

现在临时指针绑定到一个右值引用。

【讨论】:

  • 是的,我知道了。这与const int& a = 3; //yesint&a = 3; //no 的原因相同。 公式 非常有用。谢谢。当涉及到指针时,它只会变得更难识别。
  • @SkepticalEmpiricist - 是的。在接受该评论时需要考虑的重要一点是身份转换也很重要(尽管这显然不是一个非常有趣的案例)。 const int& a = 3; 从文字 int prvalue 中具体化一个临时值,并绑定对它的引用。
  • @StoryTeller 我仍然无法完全理解,我以为我知道标准中的 身份转换 意味着什么,但我无法使用它来试图理解你在说什么.能否请您详细说明一下?
  • @SkepticalEmpiricist - 我并没有试图像标准那样精确,但我的思路(和解释)试图遵循 C++17 对 prvalues 的更改,@ 987654321@ 和 this bit about initializing references.
  • @SkepticalEmpiricist - 比这更简单。临时可以所有例子中具体化。只是整个[dcl.init.ref]/5 并没有定义这样一个初始化的含义。所以程序格式不正确。
【解决方案2】:

在阅读了@StoryTeller 的精彩回答后,为无聊的人补充了一些措辞,因为我不得不对此进行不同的思考。

所以在语法上,在这两行中我们定义了一个引用a,并且在这两行中我们都有一个materialization of a temporary pointer,它接受字符串文字的地址。 两者的唯一区别是第二个const只出现在这里:

const char* const & a = "hello";

不在这里:

const char*& a = "hello";

第二个const 表示此处被引用的对象a pointer in this case, is itself const,因为它不能使用此引用进行修改。

因此,因为这个字符串文字类型const char[6](而不是const char *例如),我们的lvalue引用类型const char*第二行中的引用不能绑定到它——但第一行中的引用,作为对类型 const char* const 的引用可以。为什么?因为rules of reference initialization

(5) 对“cv1 T1”类型的引用由“cv2 T2”类型的表达式初始化,如下所示:

  • (5.1) 如果引用是左值引用和初始化表达式

    • (5.1.1) 是左值(但不是位域),“cv1 T1”与“cv2 T2”引用兼容,[...]
    • (5.1.2) 有一个类类型(即 T2 是一个类类型)[...]

两个表达式都是左值,但是我们的“cv1 T1”不是reference-compatible,我们的“cv2 T2”和“T2”不是一个类类型.

  • (5.2) 否则,如果引用是对非 const 限定类型的左值引用或 volatile 限定,则程序格式错误

引用确实不是 const-qualified:我们的“T1”是const char*,它是一个指向const的指针,而不是一个const指针。这里的实际类型是指针类型,所以这很重要。

记住这一点,第二行的 Clang 错误告诉我们:

error: non-const lvalue reference to type 'const char *' cannot bind to a value of unrelated type 'const char [6]'
    const char*& a = "hello";
                 ^    ~~~~~~~

关于成为 non-const lvalue reference 的部分正好是 ¶5.2 -- 在我们的例子中 lvalue 是一个指向 const 的指针,但它本身不是常量!与第一行不同。关于绑定到不相关类型的部分正好是¶5.1——我们的const char*兼容与RHS是const char[6]const char* const之后数组到指针的转换。

由于这个确切的原因,或者缺少这个原因,这可以无错误地编译:

char* const & a = "hello";

ISO C++11 除了警告之外,编译器会让这个通过(不应该,因为字符串文字是 'const char 6' 我们不应该放弃这是第一个const),因为引用现在是const,就其对象而言,指针。

另一个有趣的事情是一个rvalue 引用const char* && a(没有“第二个const”)可以绑定到从字符串文字实现的临时指针 em>,正如@StoryTeller 自己提供的那样。这是为什么?因为array to pointer conversion:的规则

左值或右值类型为“N T 数组” 或“未知边界数组 of T" 可以转换为类型为“pointer to T”的prvalue

此处未提及 const 或其他 cv-qualification 措辞,但这仅在我们初始化右值 ref 时有效。

【讨论】:

    最近更新 更多