应该扩展const功能吗?

时间:2009-09-08 18:58:27

标签: c++ language-features const

编辑:这个问题可能会使用更合适的标题。请随意在评论中提出建议。

在使用带有大类集的C ++时,曾经遇到const变得麻烦的情况,不是因为它的功能,而是因为它有一个非常简单的定义。它对整数或字符串的适用性是显而易见的,但对于更复杂的类,通常有多个属性可以彼此独立地修改。我想很多人都被迫了解mutable关键字可能有类似的挫折感。

对我来说最明显的例子是矩阵类,表示3D变换。矩阵将表示平移和旋转,其中每个都可以在不修改另一个的情况下进行更改。想象一下以下类和函数假设添加'多属性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);

或者想象一下预定义的const关键字,比如“_comparable”,它允许你定义随意修改对象的函数,只要你保证不会改变任何会影响对象排序顺序的东西,从而简化了对象的使用。容器

这种功能的优点和缺点是什么?你能想象在你的代码中实际使用它吗?使用当前的const关键字功能是否有一种很好的方法来实现这种功能?

记住

  • 我知道这样的语言功能很容易被滥用。许多C ++语言特性也是如此。
  • const一样,我希望这是一个严格的编译时功能。
  • 如果你已经认为const是自切片泥以来最愚蠢的事情,我会把它看成是你对此有同感。无需发帖,谢谢。

编辑: 在回应SBK关于会员加价的评论时,我建议你没有。对于标记为const的类/成员,它的工作方式与以往一样。对于标记为const(“foo”)的任何内容,它将所有成员视为可变,除非另有标记,将其留给类作者以确保其功能与广告一样有效。此外,在内部表示为2D数组的矩阵中,您不能将单个字段标记为const或非const用于平移或旋转,因为所有自由度都在单个变量声明中。

7 个答案:

答案 0 :(得分:6)

Scott Meyers正在研究一种使用仲裁限制扩展语言的系统(使用模板)。

所以你可以说功能/方法是已验证,ThreadSafe(等等或你喜欢的任何其他约束)。然后,这种约束函数只能调用具有至少(或更多)约束的其他函数。 (例如,一个方法使得ThreadSafe只能调用标记为ThreadSafe的另一个方法(除非编码器明确地抛弃了该约束)。

这是文章:
http://www.artima.com/cppsource/codefeatures.html

我喜欢的很酷的概念是在编译时强制执行约束。

答案 1 :(得分:5)

如果你有成员组合在一起或可变在一起,那么通过将它们放在一起组成自己的类来形式化它是不是很有意义?这可以在不改变语言的情况下完成。

答案 2 :(得分:3)

细化

当某个操作后ADT与其自身无法区分时,const属性适用于整个ADT。您希望定义部分常量。

在您的排序顺序示例中,您声明该运算符<在ADT的其他一些操作下,ADT是不变的。您的临时常量名称(例如“旋转”)由ADT不变的操作集定义。我们可以保留未命名的不变量,只列出const()中不变的操作。由于重载,需要使用完整的声明来指定函数。

void set_color (Color c) const (operator<, std::string get_name());
void set_name  (std::string name) const (Color get_color());

因此,const名称可以被视为形式主义 - 它们的存在或不存在不会改变系统的力量。但是'typedef'可以用来命名不变量列表,如果这证明是有用的。

typedef const(operator<, std::string get_name()) DontWorryOnlyNameChanged;

很多情况下很难想出好名字。

有用

const中的值是编译器可以检查它。这是一种不同的常量。

但我发现所有这一切都有一个很大的缺陷。从你的矩阵示例中我可能错误地推断出旋转和平移是独立的,因此是可交换的。但是存在明显的数据依赖性,矩阵乘法不是可交换的。有趣的是,这是一个例子,其中部分常数在重复应用一个或另一个但不是两者的情况下是不变的。 'translate'会惊讶地发现它的对象是由于前一次翻译后的旋转而被翻译过来的。也许我误解了旋转和翻译的含义。但这就是问题,现在似乎可以解释常量。所以我们需要......鼓声......逻辑。

逻辑

您的提案似乎与依赖类型相似。有了足够强大的类型系统,几乎任何东西都可以在编译时证明。你的兴趣在于定理证明和类型理论,而不是C ++。研究直觉主义逻辑,后续微积分,Hoare逻辑和Coq。

现在我已经完整了。命名再次有意义,

int times_2(int n) const("divisible_by_3");

因为divisible_by_3实际上是一种类型。 Here's a prime number type in Qi.欢迎来到兔子洞。我假装要到某个地方。这是什么地方?为什么这里没有时钟?

答案 3 :(得分:1)

这样的高级概念对程序员很有用。

如果我想让const-ness细粒度,我会在结构上做到:

struct C { int x; int y; };

C const<x> *c;
C const<x,y> *d;
C const& e;
C &f;

c=&e; // fail, c->y is mutable via c
d=&e;
c=&f;
d=c;

如果你允许我表示首选的最大const方法的范围(如果我的ref /指针是非const,则正常的重载更喜欢非const方法),那么编译器或独立的静态分析可以为我推断出必须成为const的成员。

当然,除非你计划实现一个预处理器,它采用漂亮的高级细粒度const C ++并将其转换为cast-away-const C ++,否则这一切都没有实际意义。我们甚至还没有C ++ 0x。

答案 4 :(得分:1)

这可能很有趣,但const的简单定义的一个有用功能是编译器可以检查它。如果您开始添加任意约束,例如“无法更改排序顺序”,则现在的编译器无法检查它。此外,在一般情况下,由于停止问题,不能解决任意约束的编译时检查的问题。我宁愿看到该功能仍然局限于编译器实际可以检查的内容。

正在努力使编译器能够检查越来越多的东西 - 复杂的类型系统(包括依赖类型系统),以及诸如在SPARKAda中完成的工作,允许编译器辅助验证各种约束 - 但它们最终都是达到了计算机科学的理论极限。

答案 5 :(得分:1)

我认为您不能将其作为严格的编译时功能来实现。

我想不出一个好的例子,所以这个严格的功能必须要做:

struct Foo{
    int bar;
};

bool operator <(Foo l, Foo r){
    return (l.bar & 0xFF) < (r.bar & 0xFF);
}

现在我将一些Foos放入一个有序集合中。显然,bar的低8位必须保持不变,以便保留顺序。然而,高位可以自由改变。这意味着集合中的Foos不是常数,但也不是可变的。但是,我没有看到任何方法可以在不使用运行时检查的情况下以一般有用的形式描述此级别的常量。

如果你形式化了我甚至可以想象的要求,你就可以证明没有能够做到这一点的编译器(在编译时)甚至可以存在。

答案 6 :(得分:0)

我认为核心语言,特别是const关键字,不适合这种情况。 C ++中const的概念旨在表达特定操作不会修改某个特定内存区域的想法。这是一个非常低级的想法。

您提出的是一个逻辑常量,它与程序的高级语义有关。正如我所看到的,主要问题是语义在不同的类和不同的程序之间可能会有很大差异,因此没有办法为这种语言提供一个适合所有语言的构造。

需要发生的是程序员需要能够编写编译器运行的验证代码,以便检查特定操作是否符合他对语义(或“逻辑”)常量的定义。但是,当你考虑它时,这样的代码,如果它在编译时运行,与单元测试没有太大区别。

你真正想要的是让编译器测试函数是否符合特定的语义契约。这就是单元测试的用途。所以你要问的是有一个语言功能可以在编译步骤中自动为你运行单元测试。考虑到系统需要多么复杂,我认为这并不是非常有用。