代码之家  ›  专栏  ›  技术社区  ›  Geezer

双重标准?为什么只对char*const&a=“bla”发出警告?

  •  4
  • Geezer  · 技术社区  · 6 年前

    在尝试钻研一些案例背后的机制之后,比如 this question 我仍然不明白为什么下面代码中的第三行只生成一个警告,而第二行是一个错误。

    int main()
    {
        const char* const& a = "bla"; // Valid code
        const char*& a2 = "bla"; // Invalid code
        char* const& a3 = "bla"; // Should be invalid but settles for a warning
    
        return 0;
    }
    

    我知道虽然 引用初始化 正在转换 字符串文字 指向指针引用,则不应删除任何 对象具有,并且转换的类型为 const char* const (从字符串文字转换而来) "bla" ,即。 const char[4] )似乎和第二条线的情况是一样的。唯一的区别是 const 被删除属于C字符串本身,而不是指针。

    在GCC 8.2和Clang 6.0.0上复制,不指定任何额外的一致性标志。

    输出自 合同通用条款

    <source>:4:23: error: cannot bind non-const lvalue reference of type 'const char*&' to an rvalue of type 'const char*'
         const char*& a2 = "Some other string literal";
                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    <source>:5:23: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
         char* const& a3 = "Yet another string literal";
    

    为什么现在的编译器符合第一种情况而不符合第二种情况?或者,这两个案例之间有没有根本的区别?

    3 回复  |  直到 6 年前
        1
  •  10
  •   melpomene    6 年前

    "bla" const char [4] .

    const char* const& a = "bla";
    

    这是有效的,因为有一个从 T [] T * ;在这种情况下,您将获得 const char * R值。此rvalue可以绑定到引用,因为它是对的引用 const (使临时工活着等)。

    const char*& a2 = "bla";
    

    无效,因为您正在尝试将临时值绑定到非常量引用。

    char* const& a3 = "bla";
    

    这是对const的引用,但类型错误(它是指向char的指针,而不是指向constchar的指针)。此转换将删除 常数 会是一个 char [4] ),因此将此设置为硬错误将破坏大量现有代码。

    char * (不是 常量字符* )仍然允许使用变量(但已弃用)。

    “双重标准”是因为从不允许将非常量引用绑定到临时对象(C甚至没有引用),因此不存在向后兼容性问题。该标准没有区分“错误”和“警告”;对于任何给定的违反规则的行为,编译是否应该成功由编译器作者自行决定。

        2
  •  3
  •   eerorika    6 年前

    这两个案例都是错误的。然而,该标准并不要求编译器拒绝格式错误的程序。因此,接受警告是完全符合标准的。

    主要区别在于,将非常量左值引用绑定到右值时,隐式转换的格式从来都不好 const char* char* 以前是良好的形成直到C++ 11。向后兼容性是允许后者的一个很好的理由。

        3
  •  2
  •   chaospower    6 年前

    常量的规则是,它总是适用于它左边的东西,除非它左边什么都没有,在这种情况下,它适用于紧靠右边的东西。对于EAST const,我们总是把const写在右边。

    让我们看看代码:

    const char* const& a = "bla"; // Valid code
    

    变成

    char const * const & a = "bla";
    

    之所以使用“char const*const”而不是“char const*”,是因为“bla”的地址是恒定的——字符串“bla”被编译到某个固定位置的执行代码中,当加载到内存中时,将一直保持在该内存地址,直到程序终止。

    所以现在我们有了匹配的类型,除了引用。

    让我们看看第二个:

    const char*& a2 = "bla"; 
    

    EAST常量语法:

    char const * & a2 = "bla";
    

    char const * const
    

    这些类型不匹配(“bla”的内存位置是固定的)。

    也许这段代码会更清楚:

    char const *stringPtr = "hello";
    char const *stringPtr2 = "world";
    
    char const * &stringPtrRef = stringPtr;
    
    std::cout << stringPtr << std::endl;
    
    stringPtrRef = stringPtr2;
    
    std::cout << stringPtr << std::endl;
    

    这将在第一行打印“Hello”,在第二行打印“World”。这是因为stringPtr指向的是变化。

    因为“bla”的位置是固定的,所以我们不能构造对它的引用,因为“bla”的位置可以通过设置对它的引用来改变。这是不可能的。我们也不可能使用强制转换来强制它成为正确的类型。

    这就是为什么即使有警告也不能编译。

    我们来看第三个:

    char* const& a3 = "bla";
    

    这已经是EAST const格式了。

    使用“char*const&时-结果引用虽然不允许更改内存位置,但允许您将“bla”修改为“abc”。

    也许在某些情况下,你真的想这样做,以节省内存空间,在一些嵌入式系统中,“bla”只是作为一个初始化,不再使用。

    这个信息很有道理:

    警告:ISO C++禁止将字符串常量转换为“char *”

    因为这与:

    char const *s1 = "bla";
    char *s2 = s1;
    

    即使没有-fppermission,我们也可以更改代码来执行强制转换并使其工作。