【问题标题】:What is the difference between "std::string const &s" and "const std::string &s"?“std::string const &s”和“const std::string &s”有什么区别?
【发布时间】:2023-06-17 18:39:01
【问题描述】:

我正在寻找有关如何做某事的示例,并看到了这两个变体:

std::string const &s;
const std::string &s;

在不同的sn-ps中。

谢谢你的回答:)

【问题讨论】:

  • 语义上没有区别。只是一个人喜欢哪种风格的问题。
  • 在第一个s 是对常量字符串的引用。第二个s 是对常量字符串的引用。 IE。两者其实是一样的。
  • 第一个更一致(const 总是在它所指的类型后面,这是它对指针起作用的唯一方式)。第二种更为常见。第一个允许您从右到左阅读声明:s 是对不可变 std::string 的引用。
  • 在去除符号时,您经常会看到 const 限定的第一种形式 - 例如,c++filt

标签: c++ c++11 reference constants notation


【解决方案1】:

std::string const & 等价于const std::string &

const std::string &是Stroustrup的C++编程语言中采用的风格,可能是“传统风格”。

std::string const & 可以比替代方案更一致:

const-on-the-right 样式总是将 const 放在它所构造的内容的右侧,而另一种样式有时将 const 放在左侧,有时放在右侧。

使用 const-on-the-right 样式,局部变量 const 使用右侧的 const 定义:int const a = 42;。同样,静态变量 const 定义为static double const x = 3.14;。基本上每个const 都在它所构成的事物的右侧结束,包括需要在右侧的const:带有一个 const 成员函数。

(详情请参阅What do “X const& x” and “X const* p” mean?)。

如果您决定使用 const-on-the-right 样式,请确保不要将 std::string const &s 错误地输入为无意义的 std::string & const s

上述声明的意思是:“sconststd::string 的引用”。 这是多余的,因为引用总是const(您永远不能重置引用以使其引用不同的对象)。

【讨论】:

    【解决方案2】:

    从技术上讲,它是相同的,没有任何区别。但是在某些调试器(肯定是 lldb)中,即使您写成 const std::string&,您也会看到 std::string const&

    【讨论】:

      【解决方案3】:

      如前所述,它们是同一类型。喜欢右边的const 的一个原因是它如何与模板一起使用。大多数人只是通过替换来处理函数模板。例如:

      template <class T> void foo(const T& arg);
      
      int* p;
      foo(p);
      

      arg 的类型是什么?你想说const int*&amp;,也就是说,引用指向const int的指针,但这是错误。文本替换让你失望了。如果你写了它而不是像

      template <class T> void foo(T const& arg);
      

      然后简单的文本替换产生正确的类型:int* const&amp;。即对const的引用指向int

      【讨论】:

        【解决方案4】:

        正如 Barry 所指出的in his answer,旧的 C 语法(以及该语法的 C++ 扩展)不支持文本替换的概念视图,就像在数学中一样。

        因此,直接使用 C 语法通常最好写 T const,而不是 const T,尽管当 T 是一个简单类型时,这些类型表达式等价。编写T const 还可以避免在像char const* const p; 这样的多级指针声明中出现不一致,其中最后一个const 不能移动。这说明等价仅适用于声明开头的基类型。

        几个月前,我开始写const T 的实验,并始终使用该符号。因为这在 C++11 之后现在可以实现,无需使用宏。为了支持一致性,我使用了命名的类型构建器,例如

        template< class Some_type >
        using Ptr_ = Some_type*;
        

        这样,而不是从右到左阅读

        char const* const p = something;
        

        我可以并且确实写出普通的阅读方向

        const Ptr_<const char> p = something;
        

        它有点冗长,但我现在有了一些使用它的经验,认为这是值得的(我不确定,这是一个实验)。

        主要缺点是,虽然这种表示法支持(函数模板)函数参数的类型推导,就像直接使用 C 语法一样,但它不支持 auto 声明的类型推导。令人高兴的是,由于可以很容易地创建一个初始化器const,所以唯一有问题的情况是对数组的引用。到目前为止,我的实用解决方案是在这种情况下直接使用 C(或者更确切地说,C++)语法,但我认为这代表了语言中的一个缺点或漏洞,可能有一些通用的解决方案可以让事情变得更容易其他情况。

        对于我当前的类型构建器,例如 Ptr_,请参阅 the cppx/core_language_support/type_builders.hpp file at Github。它们包括例如In_ 用于函数参数。是的,尽管有些冗长,但我发现这也是值得的,因为它使意图非常清晰。但是,cppx 的东西是实验性的,可能会发生变化。链接到这里的特定文件甚至可能会被移动,但它会在附近。 :)

        【讨论】:

          【解决方案5】:

          只是为了证明其他人的断言(我知道 rtti 不会将 constness 或 voltilness 带入 .name() 输出)

          这个测试程序:

           #include <string>
           #include <iostream>
          
           using std::string;
           using std::cout;
           using std::cin;
          
            using std::endl;
          
            int main()
            {
              std::string t = "BLABLA" ;
              std::string t1 = "BLABLA" ;
              std::string const &s = t;
              const std::string &d = t1;
          
          
          if (typeid(d) == typeid(s)) 
              cout << "d and s are the same type according to the rtti" << endl;
          
          else
              cout << "d and s are the NOT the same type according to the rtti" <<  endl;
              // here the raw output is exactly the same for both
              cout << typeid(d).raw_name() << endl << typeid(s).raw_name() << endl; 
          
              cin >> t;
              return 0;
          }
          

          gcc (4.9.2) (经过适当修改)msdn (vs2010) 都返回“相同类型”。

          我将此“答案”作为贡献而非“答案”。

          编辑: 阅读 Scott-Meyers 的“Effective Modern C++”中的第 4 项,分享了关于我编写的示例的一些非常有趣的隐藏信息。 简而言之,typeid 不会产生可靠的结果,因为我们是按值传递变量名。 当我们通过值传递一个方法一个变量时,推导的类型失去了它的常量性、易变性和引用性(因为上帝的爱将被缩短为 cvr)。这就是为什么上面的例子并不能证明这两种类型之间是否存在差异。

          同一个 Item 指出 Boost 项目托管了一个名为 TypeIndex 的库,它保留了 cvr 属性。

          【讨论】:

          • 投反对票的人 - 我找到了我错的地方,几乎没有感谢你。