C++的概念和Rust的特质有哪些相似之处和不同之处?

68
在Rust中,抽象的主要工具是traits。在C++中,有两种抽象工具:抽象类和模板。为了摆脱使用模板的一些缺点(例如难以阅读的错误消息),C++引入了概念,它们是"命名的需求集"
这两个特性看起来相当相似:
- 定义trait/concept是通过列出要求来完成的。 - 两者都可以用于限制泛型/模板类型参数。 - Rust traits和带有概念的C++模板都是单态化的(我知道Rust traits也可以与动态调度一起使用,但那是另一个故事)。
但据我所知,也存在显着的差异。例如,C++的概念似乎定义了一组必须有效的表达式,而不是列出函数签名。但是,那里有很多不同和令人困惑的信息(可能是因为概念只在C++20中推出?)。这就是为什么我想知道:C++的概念和Rust的traits之间到底有什么区别和相似之处? 是否有任何只由概念或traits提供的功能?例如,Rust的关联类型和常数如何处理?或将类型限定为多个traits/concepts?

12
提前声明:我知道这个问题很可能会被关闭,因为它太宽泛了。如果大多数人都这么认为,那就没事了。但是,我认为这个问题对于这个网站来说还是可以的(它是关于两个特定功能的相当具体的问题),而且我不认为将这个问题分成多个子问题是有用的。 - Lukas Kalbertodt
3
我曾经也是他们之一,我认为这种问题并不切实际,无法得到恰当的回答,并且通常也不适合SO的格式。我更希望能看到两种语言上述特性的详细比较——比如说在某个文章中,而不是简短的回答。我认为@Shepmaster的链接已经很好地总结了这一点。 - Peter Varo
2
感谢您的回答!@NeilButterworth请注意,我并没有问为什么它们不同。我只是询问事实上的区别。 - Lukas Kalbertodt
4
您认为如何更好地提出这个问题?目前看来,任何回答者都需要对Rust和C++非常熟悉。例如,您轻描淡写地提到了“Rust的关联类型和常量”,但是C++专家可能不知道这些细节。这意味着合格人员的范围将非常有限。如果您描述您所知道技术的所有细节,可能会使问题变得更容易。 - Shepmaster
10
作为一个相关的类比问题,目前在Stack Overflow上得分为130分的问题是,Rust中的traits与Haskell中的typeclasses之间有什么区别?这个问题获得了较高的关注度。 - Shepmaster
显示剩余9条评论
1个回答

49
  • Methods can be called on a variable of a generic type regardless of whether or not the type satisfies a concept.
  • Concepts only come into play when we want to constrain a type that will fit the requirements of a particular algorithm; they provide a way to express "soft" requirements.

  • 一个泛型类型的变量可以调用方法,而无需满足任何Concept或以任何方式受限。
  • 一个泛型类型的变量可以调用满足Concept(或多个)的方法,而不需要这种方法被任何Concept或约束所指定。
  • 约束(见注释)可以完全是临时的,可以在不使用命名Concept的情况下指定要求;再次强调,它们完全是可选的。
  • 注意:约束由requires子句引入,并指定基于概念的要求或临时要求。

    要求

    可表达的要求集合不同:

    • Concepts/Constraints通过替换工作,因此允许语言的全部广度;要求包括:嵌套类型/常量/变量、方法、字段、能够作为另一个函数/方法的参数使用、能够作为另一个类型的泛型参数使用,以及其组合。
    • Traits只允许一小部分要求:关联类型/常量和方法。

    重载选择

    Rust没有临时重载的概念,重载仅由Traits发生,特化目前还不可能。

    C++的约束(Constraints)可以用于对重载函数进行“排序”,从最不特定到最特定,这样编译器就能自动选择符合要求的最特定的重载函数。

    注意:在此之前,C++中要使用SFINAE或标签分派(tag-dispatching)来实现重载函数的选择;需要进行一些体操才能处理开放式的重载函数集。

    析取(Disjunction)

    如何使用这个功能还不太清楚。

    Rust中的需求机制是纯加法(conjunctions, 即&&),相比之下,在C++中requires子句可以包含析取(disjunctions, 即||)。


    1
    “结构化类型比第三方库更好的桥梁”,前提是要求以适应类型为基础。如果需要在类型上调用“read”成员,但库只提供了“Read”,那么你几乎无能为力。Rust的“impl”块可以进行适配。 - Sebastian Redl
    1
    @LukasKalbertodt:在C++概念中没有进行Hindley Milner类型推断,所以不行。 - Matthieu M.
    1
    @SebastianRedl:当然可以。但这就是需要始终使用适配器结构体(Rust)与仅需要使用适配器结构体来消除差异(C++)之间的区别。此外,使用自由函数而不是成员函数只需要添加一个函数(在这里是read,它委托给Read)。 - Matthieu M.
    1
    C++ 中是否有类似于关联常量或关联类型等其他相关项可以进行比较的内容? - Ralf Jung
    2
    @RalfJung:在C++中,这将是静态成员变量(常量)和嵌套的typedef或嵌套类。 - Matthieu M.
    显示剩余5条评论

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