是否应该扩展 const 功能?
编辑:这个问题可能可以使用更合适的标题。请随意在评论中推荐一个。
在将 C++ 与大型类集一起使用时,我曾经遇到过这样一种情况:const 变得很麻烦,不是因为它的功能,而是因为它的定义非常简单。它对整数或字符串的适用性是显而易见的,但对于更复杂的类,通常有多个属性可以相互独立地修改。我想许多被迫学习 mutable
关键字的作用的人可能也有过类似的挫败感。
对我来说最明显的例子是矩阵类,代表 3D 变换。矩阵将表示平移和旋转,其中每一个都可以在不修改另一个的情况下进行更改。想象一下下面的类和函数,假设添加了“多属性常量”。
class Matrix {
void translate(const Vector & translation) const("rotation");
void rotate(const Quaternion & rotation) const("translation");
}
public void spin180(const("translation") & Matrix matrix);
public void moveToOrigin(const("rotation") & Matrix matrix);
或者想象一下预定义的 const 关键字,如“_comparable”,它允许您定义随意修改对象的函数,只要您保证不更改任何会影响对象排序顺序的内容,从而简化排序容器中对象的使用。
这种功能有哪些优点和缺点?您能想象它在您的代码中的实际用途吗?有没有一种好的方法可以使用当前的 const 关键字功能来实现这种功能?
请记住,
- 我知道这样的语言功能很容易被滥用。许多 C++ 语言功能也是如此,
- 例如
const
我希望这是一个严格的编译时功能。 - 如果你已经认为 const 是自切片泥以来最愚蠢的事情,我会认为你对此也有同样的感觉。无需发帖,谢谢。
编辑: 针对 SBK 关于会员加价的评论,我建议您不要有任何加价。对于标记为 const 的类/成员,它的工作方式与往常一样。对于任何标记为 const("foo") 的内容,除非另有标记,否则它将所有成员视为可变的,从而由类作者来确保其函数按照广告中的方式工作。此外,在内部表示为 2D 数组的矩阵中,您无法将各个字段标记为常量或非常量以进行平移或旋转,因为所有自由度都位于单个变量声明内。
EDIT: this question could probably use a more apropos title. Feel free to suggest one in the comments.
In using C++ with a large class set I once came upon a situation where const
became a hassle, not because of its functionality, but because it's got a very simplistic definition. Its applicability to an integer or string is obvious, but for more complicated classes there are often multiple properties that could be modified independently of one another. I imagine many people forced to learn what the mutable
keyword does might have had similar frustrations.
The most apparent example to me would be a matrix class, representing a 3D transform. A matrix will represent both a translation and a rotation each of which can be changed without modifying the other. Imagine the following class and functions with the hypothetical addition of 'multi-property const'.
class Matrix {
void translate(const Vector & translation) const("rotation");
void rotate(const Quaternion & rotation) const("translation");
}
public void spin180(const("translation") & Matrix matrix);
public void moveToOrigin(const("rotation") & Matrix matrix);
Or imagine predefined const keywords like "_comparable" which allow you to define functions that modify the object at will as long as you promise not to change anything that would affect the sort order of the object, easing the use of objects in sorted containers.
What would be some of the pros and cons of this kind of functionality? Can you imagine a practical use for it in your code? Is there a good approach to achieving this kind of functionality with the current const keyword functionality?
Bear in mind
- I know such a language feature could easily be abused. The same can be said of many C++ language features
- Like
const
I would expect this to be a strictly compile-time bit of functionality. - If you already think
const
is the stupidest thing since sliced mud, I'll take it as read that you feel the same way about this. No need to post, thanks.
EDIT:
In response to SBK's comment about member markup, I would suggest that you don't have any. For classes / members marked const, it works exactly as it always has. For anything marked const("foo") it treats all the members as mutable unless otherwise marked, leaving it up to the class author to ensure that his functions work as advertised. Besides, in a matrix represented as a 2D array internally, you can't mark the individual fields as const or non-const for translation or rotation because all the degrees of freedom are inside a single variable declaration.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
Scott Meyers 正在研究一种可以通过任意约束(使用模板)扩展语言的系统。
所以你可以说一个函数/方法是经过验证的,线程安全的(等等或你喜欢的任何其他约束)。那么这样的受约束的函数只能调用其他至少有(或更多)约束的函数。 (例如,创建 ThreadSafe 的方法只能调用另一个标记为 ThreadSafe 的方法(除非编码器明确放弃该约束)。
这是文章:
http://www.artima.com/cppsource/codefeatures.html
很酷的概念我喜欢的是在编译时强制执行约束。
Scott Meyers was working on a system of expanding the language with arbitary constraints (using templates).
So you could say a function/method was Verified,ThreadSafe (etc or any other constraints you liked). Then such constrained functions could only call other functions which had at least (or more) constraints. (eg a method maked ThreadSafe could only call another method marked ThreadSafe (unless the coder explicitly cast away that constraint).
Here is the article:
http://www.artima.com/cppsource/codefeatures.html
The cool concept I liked was that the constraints were enforced at compile time.
如果您的成员组要么是 const 在一起,要么是 mutable 在一起,那么通过将它们放在自己的类中来正式化不是很有意义吗?今天就可以在不改变语言的情况下做到这一点。
In cases where you have groups of members that are either const together or mutable together, wouldn't it make as much sense to formalize that by putting them in their own class together? That can be done today without changing the language.
细化
当某个 ADT 在某些操作后与自身无法区分时,const 属性适用于整个 ADT。您希望定义部分常量。
在您的排序顺序示例中,您断言该运算符< ADT 的值在 ADT 上的某些其他操作下是不变的。您的临时常量名称(例如“旋转”)由 ADT 不变的操作集定义。我们可以不命名不变式,只列出 const() 内不变式的操作。由于重载函数需要用完整的声明来指定。
因此,const 名称可以被视为一种形式主义 - 它们的存在或不存在不会改变系统的功能。但如果证明有用的话,“typedef”可以用来命名不变量列表。
对于许多情况来说,很难想出好的名字。
有用性
const 中的值是编译器可以检查它的。这是一种不同类型的常量。
但我发现这一切有一个很大的缺陷。从你的矩阵示例中,我可能错误地推断旋转和平移是独立的,因此是可交换的。但存在明显的数据依赖性,并且矩阵乘法不可交换。有趣的是,这是一个例子,其中部分常量在重复应用其中一个而不是两者的情况下保持不变。 “translate”会惊讶地发现它的对象由于上次翻译后的旋转而被翻译了。也许我误解了旋转和平移的含义。但这就是问题所在,这种常量现在似乎可以解释。所以我们需要……击鼓……逻辑。
逻辑
看来您的建议类似于依赖类型。有了足够强大的类型系统,几乎任何事情都可以在编译时得到证明。您的兴趣在于定理证明者和类型论,而不是 C++。研究直觉逻辑、序列微积分、霍尔逻辑和 Coq。
现在我已经回到原点了。命名再次有意义,
因为 divisible_by_3 实际上是一种类型。 这是 Qi 中的质数类型。欢迎来到兔子洞。我假装已经有所进展。这是什么地方?这里怎么没有钟?
Refinement
When an ADT is indistinguishable from itself after some operation the const property holds for the entire ADT. You wish to define partial constness.
In your sort order example you are asserting that operator< of the ADT is invariant under some other operation on the ADT. Your ad-hoc const names such as "rotation" are defined by the set of operations for which the ADT is invariant. We could leave the invariant unnamed and just list the operations that are invariant inside const(). Due to overloading functions would need to be specified with their full declaration.
So the const names can be seen as a formalism - their existence or absence doesn't change the power of the system. But 'typedef' could be used to name a list of invariants if that proves useful.
It would be hard to think of good names for many cases.
Usefulness
The value in const is that the compiler can check it. This is a different kind of const.
But I see one big flaw in all of this. From your matrix example I might incorrectly infer that rotation and translation are independent and therefore commutative. But there is an obvious data dependency and matrix multiplication is not commutative. Interestingly, this is an example where partial constness is invariant under repeated application of one or the other but not both. 'translate' would be surprised to find that it's object had been translated due to a rotation after a previous translation. Perhaps I am misunderstanding the meaning of rotate and translate. But that's the problem, that constness now seems open to interpretation. So we need ... drum roll ... Logic.
Logic
It appears your proposal is analogous to dependent typing. With a powerful enough type system almost anything is provable at compile time. Your interest is in theorem provers and type theory, not C++. Look into intuitionistic logic, sequent calculus, Hoare logic, and Coq.
Now I've come full circle. Naming makes sense again,
since divisible_by_3 is actually a type. Here's a prime number type in Qi. Welcome to the rabbit hole. And I pretended to be getting somewhere. What is this place? Why are there no clocks in here?
这些高级概念对于程序员来说非常有用。
如果我想让 const 细粒度化,我会在结构上做到这一点:
如果你允许我表达对最大 const 方法是首选的范围的偏好(如果我的 ref ,则正常重载会更喜欢非 const 方法) /pointer 是非 const),那么编译器或独立的静态分析可以为我推断出必须是 const 的成员集。
当然,除非您计划实现一个预处理器,该预处理器采用高级细粒度 const C++ 并将其转换为抛弃型 const C++,否则这一切都是没有意义的。我们甚至还没有 C++0x。
Such high level concepts are useful for a programmer.
If I wanted to make const-ness fine-grained, I'd do it structurally:
If you allow me to express a preference for a scope that maximally const methods are preferred (the normal overloading would prefer the non-const method if my ref/pointer is non-const), then the compiler or a standalone static analysis could deduce the sets of must-be-const members for me.
Of course, this is all moot unless you plan on implementing a preprocessor that takes the nice high-level finely grained const C++ and translates it into casting-away-const C++. We don't even have C++0x yet.
我认为您不能通过严格的编译时功能来实现这一点。
我想不出一个好的例子,所以这个严格的功能性例子必须要做:
现在我将一些 Foos 放入一个排序集中。显然,bar 的低 8 位必须保持不变,以便保留顺序。然而,高位可以自由更改。这意味着集合中的 Foos 不是 const,但也不是可变的。然而,我看不出有什么方法可以在不使用运行时检查的情况下以通用有用的形式描述这种常量级别。
如果您将我能想象到的要求形式化,您就可以证明不存在能够(在编译时)执行此操作的编译器。
I don't think that you can achieve this as strictly compile-time functionality.
I can't think of a good example so this strictly functional one will have to do:
Now I put a some Foos into a sorted set. Obviously the lower 8 bits of bar must remain unchanged so that the order is preserved. The upper bits can however be freely changed. This means the Foos in the set aren't const but aren't mutable either. However I don't see any way you could describe this level of constness in a general useful form without using runtime checking.
If you formalized the requirements I could even imagine, that you could prove that no compiler capable of doing this (at compile time) could even exist.
这可能很有趣,但是 const 简单定义的有用功能之一是编译器可以检查它。如果您开始添加任意约束,例如“无法更改排序顺序”,则编译器现在无法检查它。此外,在一般情况下,由于暂停问题,任意约束的编译时检查问题是不可能解决的。我宁愿看到该功能仍然仅限于编译器实际可以检查的内容。
人们正在努力使编译器能够检查越来越多的东西——复杂的类型系统(包括依赖类型系统),以及诸如在 SPARKDa 中所做的工作,允许编译器辅助验证各种约束——但它们最终都达到了理论水平计算机科学的局限性。
It could be interesting, but one of the useful features of
const
's simple definition is that the compiler can check it. If you start adding arbitrary constraints, such as "cannot change sort order", the compiler as it stands now cannot check it. Further, the problem of compile-time checking of arbitrary constraints is, in the general case, impossible to solve due to the halting problem. I would rather see the feature remain limited to what can actually be checked by a compiler.There is work on enabling compilers to check more and more things — sophisticated type systems (including dependent type systems), and work such as the that done in SPARKAda, allowing for compiler-aided verification of various constraints — but they all eventually hit the theoretical limits of computer science.
我认为核心语言,尤其是
const
关键字,不适合这样做。 C++ 中 const 的概念旨在表达特定操作不会修改内存的特定区域的想法。这是一个非常低级的想法。您所提议的是与程序的高级语义有关的逻辑常量。在我看来,主要问题是不同类和不同程序之间的语义差异很大,因此不可能有一种通用的语言结构。
需要发生的是,程序员需要能够编写编译器运行的验证代码,以检查特定操作是否符合他对语义(或“逻辑”)常量的定义。但仔细想想,这样的代码如果在编译时运行,则与单元测试没有太大区别。
您真正想要的是编译器测试函数是否遵守特定的语义契约。这就是单元测试的用途。因此,您要求的是有一种语言功能可以在编译步骤中自动为您运行单元测试。我认为考虑到系统需要多么复杂,这并不是很有用。
I don't think the core language, and especially the
const
keyword, would be the right place for this. The concept ofconst
in C++ is meant to express the idea that a particular action will not modify a certain area of memory. It is a very low-level idea.What you are proposing is a logical const-ness that has to do with the high-level semantics of your program. The main problem, as I see it, is that semantics can vary so much between different classes and different programs that there would be no way for there to be a one-size-fits all language construct for this.
What would need to happen is that the programmer would need to be able to write validation code that the compiler would run in order to check that particular operations met his definition of semantic (or "logical") const-ness. When you think about it, though, such code, if it ran at compile-time, would not be very different from a unit test.
Really what you want is for the compiler to test whether functions adhere to a particular semantic contract. That's what unit tests are for. So what you're asking is that there be a language feature that automatically runs unit tests for you during the compilation step. I think that's not terribly useful, given how complicated the system would need to be.