为什么const正确性特定于C ++? [英] Why is const-correctness specific to C++?

查看:126
本文介绍了为什么const正确性特定于C ++?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

免责声明:我知道有两个关于const正确性的有用性的问题,然而,没有讨论过在C ++ 而不是其他编程语言中需要const正确性。对这些问题的答案不满意。



我现在使用了几种编程语言,而在C ++中有一件事就是const-正确性。在Java,C#,Python,Ruby,Visual Basic等等中没有这样的概念,这似乎是非常具体的C ++。



C ++ FAQ Lite,我读过它,它不能说服我。完全有效,可靠的程序都是用Python编写的,并且没有const关键字或等价物。在Java和C#中,对象可以声明为final(或const),但没有const成员函数或const函数参数。如果一个函数不需要修改一个对象,它可以采用一个只提供对该对象的读访问的接口。该技术同样可以在C ++中使用。在我开发的两个现实世界的C ++系统上,const在任何地方都很少使用,一切运行正常。所以我远远没有出售让const污染代码库的有用性。



我想知道C ++中的什么是const,而不是其他编程语言。

到目前为止,我只看到一个使用const 的情况:

  #include< iostream> 

struct Vector2 {
int X;
int Y;
};

void display(/ * const * / Vector2& vect){
std :: cout< vect.X < < vect.Y< std :: endl;
}

int main(){
display(Vector2());
}

使用const注释掉的语句被Visual Studio接受,但是警告< a href =http://msdn.microsoft.com/en-us/library/186yxbac%28VS.80%29.aspx> C4239 ,使用非标准扩展。所以,如果你想要简单的句法传递临时,避免副本,并保持标准兼容,你必须传递const引用,没有办法。



否则,实际上没有必须使用const的情况,除非与其他代码交互使用const。在我看来,除了一个自我正义的瘟疫,它传播到它触及的一切:


在C ++中const工作的原因是
因为你可以把它丢掉。如果你
不能把它丢弃,那么你的世界
会吸。如果你声明一个方法
需要一个const Bla,你可以传递
它一个非const Bla。但如果是
其他方式你不能。如果你
声明一个接受
非const Bla的方法,你不能传递它
const Bla。所以现在你被卡住了。所以你
逐渐需要一个const版本的
一切不const,你
结束了一个影子世界。在C ++中,
可以使用它,因为和
一样,C ++中的任何东西都是纯可选的
是否需要此检查。




如果你不喜欢它,你可以直接使用常量
。Anders Hejlsberg(C#建筑师), CLR设计选择

解决方案

好吧,我需要6年才能真正理解,但现在我终于可以回答自己的问题了。



C ++的原因是 const-correctness,Java,C#等不支持C ++只支持值类型,这些其他语言只支持或至少默认引用类型



让我们来看看C#是一种默认为引用类型的语言,在涉及值类型时处理不可变性。假设您有一个可变的值类型,以及另一个类型为readonly的类型:

  struct Vector {
public int X {get;私人集}
public int Y {get;私人集}
public void Add(int x,int y){
X + = x;
Y + = y;
}
}

class Foo {
readonly Vector _v;
public void Add(int x,int y)=> _v.Add(x,y);
public override string ToString()=> ${_ v.X} {_v.Y};
}

void Main()
{
var f = new Foo();
f.Add(3,4);
Console.WriteLine(f);
}

此代码应该做什么?


  1. 无法编译

  2. 打印3,4

  3. 打印0,0 / li>

答案是#3。 C#尝试通过调用方法添加对象的丢弃副本来荣誉你的readonly关键字。这很奇怪,是的,但是有什么其他的选择呢?如果它调用原始Vector上的方法,对象将改变,违反字段的readonly的性质。如果它无法编译,则readonly值类型成员是很无用的,因为你不能调用任何方法对他们,出于担心他们可能会改变对象。



如果只有我们可以标记哪些方法可以安全地调用readonly实例...等等,这正是C ++中的const方法!



C#不需要使用const方法,因为我们不使用C#中的值类型;我们只是避免可变的值类型(并声明它们邪恶,参见 1 2 )。



,引用类型不会遇到此问题,因为当您将引用类型变量标记为只读时,只读是引用,而不是对象本身。这很容易让编译器执行,它可以将任何分配标记为编译错误,除非在初始化。如果你使用的是引用类型,并且所有的字段和变量都是只读的,那么在任何地方都可以获得不变性,而且语法成本很低。 F#完全像这样。 Java避免了这个问题,只是不支持用户定义的值类型。



C ++没有引用类型的概念,只有值类型 -lingo);这些值类型中的一些可以是指针或引用,但是类似于C#中的值类型,它们都不具有它们的存储空间。如果C ++对它的类型处理了const,C#对于值类型处理只读的方式,它将是非常混乱,如上面的例子所示,永远不会讨厌与拷贝构造函数的交互。



所以C ++不会创建一个丢弃的副本,因为这将创建无尽的痛苦。它不禁止你在成员上调用任何方法,因为,好吧,语言不会那么有用。但它仍然希望有一些readonly或constness的概念。



C ++试图找到一个中间的方法,通过标记哪些方法是安全的调用const成员,然后它相信你已经忠实和准确的在您的标签和直接调用原始对象的方法。这不是完美的 - 它是冗长的,你被允许违反常数,尽可能多,但它可以说比其他所有选项更好。


Disclaimer: I am aware that there are two questions about the usefulness of const-correctness, however, none discussed how const-correctness is necessary in C++ as opposed to other programming languages. Also, I am not satisfied with the answers provided to these questions.

I've used a few programming languages now, and one thing that bugs me in C++ is the notion of const-correctness. There is no such notion in Java, C#, Python, Ruby, Visual Basic, etc., this seems to be very specific to C++.

Before you refer me to the C++ FAQ Lite, I've read it, and it doesn't convince me. Perfectly valid, reliable programs are written in Python all the time, and there is no const keyword or equivalent. In Java and C#, objects can be declared final (or const), but there are no const member functions or const function parameters. If a function doesn't need to modify an object, it can take an interface that only provides read access to the object. That technique can equally be used in C++. On the two real-world C++ systems I've worked on, there was very little use of const anywhere, and everything worked fine. So I'm far from sold on the usefulness of letting const contaminate a codebase.

I am wondering what is it in C++ that makes const necessary, as opposed to other programming languages.

So far, I've seen only one case where const must be used:

#include <iostream>

struct Vector2 {
    int X;
    int Y;
};

void display(/* const */ Vector2& vect) {
    std::cout << vect.X << " " << vect.Y << std::endl;
}

int main() {
    display(Vector2());
}

Compiling this with const commented out is accepted by Visual Studio, but with warning C4239, non-standard extension used. So, if you want the syntactic brevity of passing in temporaries, avoiding copies, and staying standard-compliant, you have to pass by const reference, no way around it. Still, this is more like a quirk than a fundamental reason.

Otherwise, there really is no situation where const has to be used, except when interfacing with other code that uses const. Const seems to me little else than a self-righteous plague that spreads to everything it touches :

The reason that const works in C++ is because you can cast it away. If you couldn't cast it away, then your world would suck. If you declare a method that takes a const Bla, you could pass it a non-const Bla. But if it's the other way around you can't. If you declare a method that takes a non-const Bla, you can't pass it a const Bla. So now you're stuck. So you gradually need a const version of everything that isn't const, and you end up with a shadow world. In C++ you get away with it, because as with anything in C++ it is purely optional whether you want this check or not. You can just whack the constness away if you don't like it.

Anders Hejlsberg (C# architect), CLR Design Choices

解决方案

Well, it will have taken me 6 years to really understand, but now I can finally answer my own question.

The reason C++ has "const-correctness" and that Java, C#, etc. don't, is that C++ only supports value types, and these other languages only support or at least default to reference types.

Let's see how C#, a language that defaults to reference types, deals with immutability when value types are involved. Let's say you have a mutable value type, and another type that has a readonly field of that type:

struct Vector {
    public int X { get; private set; }
    public int Y { get; private set; }
    public void Add(int x, int y) {
        X += x;
        Y += y;
    }
}

class Foo {
    readonly Vector _v;
    public void Add(int x, int y) => _v.Add(x, y);
    public override string ToString() => $"{_v.X} {_v.Y}";
}

void Main()
{
    var f = new Foo();
    f.Add(3, 4);
    Console.WriteLine(f);
}

What should this code do?

  1. fail to compile
  2. print "3, 4"
  3. print "0, 0"

The answer is #3. C# tries to honor your "readonly" keyword by invoking the method Add on a throw-away copy of the object. That's weird, yes, but what other options does it have? If it invokes the method on the original Vector, the object will change, violating the "readonly"-ness of the field. If it fails to compile, then readonly value type members are pretty useless, because you can't invoke any methods on them, out of fear they might change the object.

If only we could label which methods are safe to call on readonly instances... Wait, that's exactly what const methods are in C++!

C# doesn't bother with const methods because we don't use value types that much in C#; we just avoid mutable value types (and declare them "evil", see 1, 2).

Also, reference types don't suffer from this problem, because when you mark a reference type variable as readonly, what's readonly is the reference, not the object itself. That's very easy for the compiler to enforce, it can mark any assignment as a compilation error except at initialization. If all you use is reference types and all your fields and variables are readonly, you get immutability everywhere at little syntactic cost. F# works entirely like this. Java avoids the issue by just not supporting user-defined value types.

C++ doesn't have the concept of "reference types", only "value types" (in C#-lingo); some of these value types can be pointers or references, but like value types in C#, none of them own their storage. If C++ treated "const" on its types the way C# treats "readonly" on value types, it would be very confusing as the example above demonstrates, nevermind the nasty interaction with copy constructors.

So C++ doesn't create a throw-away copy, because that would create endless pain. It doesn't forbid you to call any methods on members either, because, well, the language wouldn't be very useful then. But it still wants to have some notion of "readonly" or "const-ness".

C++ attempts to find a middle way by making you label which methods are safe to call on const members, and then it trusts you to have been faithful and accurate in your labeling and calls methods on the original objects directly. This is not perfect - it's verbose, and you're allowed to violate const-ness as much as you please - but it's arguably better than all the other options.

这篇关于为什么const正确性特定于C ++?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆