“std::string const &s” 和 “const std::string &s” 有何区别?(这是一个关于IT技术的提问标题)

26

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

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

在不同的片段中。

谢谢你的回答 :)


6
从语义上来说没有区别,只是一个人喜欢哪种风格的问题。 - Daniel Jour
3
第一个s是指对一个常量字符串的引用。而第二个s是指对一个不可变的字符串的引用。也就是说,它们实际上是相同的意思。 - Some programmer dude
3
相关主题:https://dev59.com/E2035IYBdhLWcg3wTuJu https://dev59.com/0HM_5IYBdhLWcg3w-4dg这些链接讨论了在C++中使用const的不同方式和语法。其中一个问题是是否将const关键字放在类型名称之前或之后,另一个问题则讨论了int const、const int和const int const之间的区别。理解如何在C++中正确使用const是编写高质量代码的关键。 - dragosht
1
第一种更加一致(const 总是在它所指向的类型之后,这也是它对指针起作用的唯一方式)。第二种更为常见。第一种允许你从右到左阅读声明:s 是一个不可变的 std::string 的引用。 - kamikaze
当反编译符号时,您经常会看到第一种const限定符形式 - 例如c++filt - Brett Hale
5个回答

21

std::string const & 等同于 const std::string &

const std::string & 是斯特劳斯特鲁普(Stroustrup)在《C++程序设计语言》中采用的风格,可能是“传统风格”。

std::string const & 可能比另一种风格更加一致:

const-on-the-right 风格总是将 常量(const) 放在它所修饰的对象的右侧,而其他风格有时会将 常量(const) 放在左侧,有时则放在右侧。

使用 const-on-the-right 风格,定义一个本地变量为常量,常量位于右侧:int const a = 42;。同样地,定义一个静态变量为常量,常量位于右侧:static double const x = 3.14;。基本上,每个常量都出现在其所修饰的对象的右侧,包括必须位于右侧的常量:具有常量成员函数的对象。

(请参见“X const& x” 和 “X const* p” 是什么意思?了解更多详细信息)。

如果你决定使用 const-on-the-right 风格,请确保不要将 std::string const &s 打成毫无意义的 std::string & const s

上述声明的含义是: "s是一个指向std::string的常量引用"。 这是多余的,因为引用总是 const 的(你永远不能重置引用以使其引用其他对象)。


7
正如所述,它们是同一类型。将const放在右侧的原因之一是它与模板的配合。大多数人通过简单的替换来处理函数模板。例如:
template <class T> void foo(const T& arg);

int* p;
foo(p);
arg的类型是什么?你想表达的是const int*&,也就是指向const int指针的引用,但那是错误的。文本替换让你失望了。如果你将其写成下面这样会更好:
template <class T> void foo(T const& arg);

然后进行简单的文本替换即可得到正确的类型:int* const&。也就是说,这是一个指向const整型指针的引用。

7

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


5
正如Barry在他的回答中所提到的,旧的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_),请参见Github上的cppx/core_language_support/type_builders.hpp文件。它们包括用于函数参数的In_等内容。尽管有些冗长,但是我发现这也非常值得,因为它可以使意图非常清晰。然而,cppx的东西是实验性的并且可能会更改。此处链接的特定文件甚至可能被移动,但大致在那里。 :)

3

为了证明其他人的说法(我知道rtti不会在.name()输出中考虑constness或voltilness),这只是一个测试程序:

 #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)来说,两者都返回"same type"。

我提供这个"答案"作为贡献而不是"答案"。

编辑:Scott-Meyers的《Effective Modern C++》第4项中分享了一些有关我所写示例的真正有趣的隐藏信息。 简而言之,由于我们通过值传递变量名,因此typeid无法产生可靠的结果。 当我们通过值传递方法中的变量时,推导出的类型失去其constness、volatilness和referenceness(缩写为cvr)。这就是为什么上面的示例不能证明这两种类型之间的区别。

同一项声明Boost项目托管了一个名为TypeIndex的库,它可以保留cvr属性。


4
投反对票的人 - 我发现了我错在哪里,但几乎没有得到你们的帮助。我不感谢你们。 - Aviv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接