C++继任者?Carbon语法速览!
7月19日Cpp North大会上,谷歌的C++专家Chandler Carruth发表了『C++: What Comes Next? (Announcing the Carbon Language experiment) 』的主题演讲,官宣了正在实验中的Carbon语言,其目标是成为C++的继任者。该消息迅速火爆全球,中文互联网圈在7月20号也开始大量报道这一消息。
Chandler Carruth在谷歌负责LLVM编译器的优化,也是谷歌在 C++ 委员会的代表。
目前Carbon语言在Github上已经开源,地址:https://github.com/carbon-language/carbon-lang
7月22日,Cpp North的Youtube频道公开了这一演讲的视频:https://www.youtube.com/watch?v=omrY53kbVoA&ab_channel=CppNorth
接下来让我们来速览一下Carbon的语法,首先声明本文的Carbon代码都是基于Chandler Carruth的Keynote以及当前Carbon的github仓库中的代码。
由于Carbon还在实验阶段,Chandler Carruth的Keynote中演示某些语法,当前的Carbon还不支持编译……,比如abstract class
、类外定义函数等等。当然已经实现的语法部分可能不稳定,后期也可能会再有重大调整也未可知。
由于当下并且没有支持Carbon语法的代码高亮插件,所以下面代码中的高亮并不准,敬请见谅。
Hello World
package ExplorerTest api;
第一行是定义了package(包),包名是ExplorerTest。后面的api,那个不是包名的一部分,但是又不能省略(当前可以是api或impl)。
-
fn Main() -> i32 {
-
var s: auto = "Hello world!";
-
Print(s);
-
return 0;
-
}
定义了main函数,Carbon中使用的Main()
。从这个Hello World的例子中,变量语法、函数定义语法可见一斑。
变量
var
即变量:
-
var x: i64 = 42;
-
x = 7;
笔者点评:个人感觉还不如C++的语法简洁……
int64_t x = 42;
冒号后面是变量的类型,i64是int64,也可以使用auto做类型推导:
var x: auto = 42;
let
基本等同于C++中常量,Rust也采用let
表示常量。
let x: i64 = 42;
同样也支持auto
let x: auto = 42;
控制流
if else
-
if (fruit.IsYellow()) {
-
Console.Print("Banana!");
-
} else if (fruit.IsOrange()) {
-
Console.Print("Orange!");
-
} else {
-
Console.Print("Vegetable!");
-
}
和C/C++一样。
while
-
var x: i32 = 0;
-
while (x < 3) {
-
Console.Print(x);
-
++x;
-
}
-
Console.Print("Done!");
也和C++一样
for
-
for (var step: Step in steps) {
-
if (step.IsManual()) {
-
Console.Print("Reached manual step!");
-
break;
-
}
-
if (step.NotReady()) {
-
continue;
-
}
-
step.Process();
-
}
语法是for-range循环的语法。同样也能发现Carbon支持break和continue。
match
Carbon中没有switch,但是有match:
-
fn Bar() -> (i32, (f32, f32));
-
-
fn Foo() -> f32 {
-
match (Bar()) {
-
case (42, (x: f32, y: f32)) => {
-
return x - y;
-
}
-
case (p: i32, (x: f32, _: f32)) if (p < 13) => {
-
return p * x;
-
}
-
case (p: i32, _: auto) if (p > 3) => {
-
return p * Pi;
-
}
-
default => {
-
return Pi;
-
}
-
}
-
}
不过C++中的switch能比较的只能是整型、枚举。
函数
-
fn Add(a: i64, b: i64) -> i64 {
-
return a + b;
-
}
Carbon中这个Add函数的写法和Rust中实现一个Add几乎一模一样。比如声明参数的时候类型在后,并且冒号分割的参数写法。还有fn
关键字。
当然在函数体内逻辑复杂的时候,会和Rust不同,因为Rust还有不带分号的表达式语法,表达式的值就是整个函数的值。Carbon没有那种怪异的东西。
另外Carbon也能进行返回值的类型推导:
-
fn Add(a: i64, b: i64) -> auto {
-
return a + b;
-
}
一个更复杂的函数的例子:
-
package Geometry api;
-
-
import Math; // 导入其他包
-
-
class Circle { // 定义一个Circle类
-
var r: f32;
-
}
-
-
fn ScaleAreaAndAppend(circle: Circle, log2_scale: i32,
-
results: Vector(f32)*) {
-
var area: f32 = Math.Pi * c.r * c.r;
-
let scale: i32 = 1 << log2_scale;
-
area *= scale;
-
results->append(area);
-
}
参数默认都是常量,除非声明成指针类型。
面向对象
类与对象
-
class Point {
-
var x: i32;
-
var y: i32;
-
}
-
-
fn Main() -> i32 {
-
var p1: Point = {.x = 1, .y = 2};
-
var p2: auto = p1;
-
p2.x = 3;
-
return p1.x - 1;
-
}
成员变量与成员函数
-
class NewsAriticle {
-
// 类似C++的static
-
fn Make(headline: String, body_html: String) -> NewsAritcle();
-
-
// 只读方法
-
fn AsHtml[me: Self]() -> String;
-
-
// 可修改方法
-
fn Publish[addr me: Self*]() { me->published = DateTime.Now(); }
-
-
private var headline: String;
-
private var body_html: String;
-
private var published: Optional(Datetime);
-
}
-
// 类外定义成员函数
-
fn NewsAriticle.AsHtml[me: Self]() -> String{ ... }
Carbon中类中成员访问控制权限默认都是public
,如果需要声明成私有则需要单独加private
关键字。这个行为和C/C++的struct相同,但是和主流语言的class都不同。
定义在类中的函数,如果有[me: Self]
表示是只读的成员函数,在函数中不能修改类对象的成员变量。me在函数体中是表示对当前对象的引用,类似C++的(*this)
。
如果有[addr me: Self*]
表示的是可对当前对象进行修改的函数。me在函数体中类似C++的this
指针。
[me: Self]
或[addr me: Self*]
的成员函数,也可以称作方法(method),如果类中的函数没有[me: Self]
或[addr me: Self*]
,则表示是一个和对象无关的函数,等价于C++中的static成员函数。这个设计很像python中的类中成员函数的设计。
继承与抽象
Carbon只支持单继承,这没的说。值得注意的是普通的class
关键字定义的类型默认都是final
的,即不能被继承生成子类(俗称『绝育』)。但abstrct class
和base class
关键字定义的类型可以被继承:
-
// 抽象类(abstract class)不能被实例化,因为其中可能包含抽象方法
-
abstract class UIWidget {
-
// 抽象方法(abstract fn)没有实现
-
abstract fn Draw[me: Self](s: Screen);
-
abstract fn Click[addr me: Self*](x: i32, y: i32);
-
}
-
-
// base class 允许扩展和实例化
-
base class Button extends UIWidget {
-
// 实现抽象方法
-
impl fn Draw[me: Self](s: Screen) { ... }
-
impl fn Click[addr me: Self*];
-
-
// 新增了一个虚函数(virtual fn)
-
virtual fn MoveTo[addr me: Self*](x: i32, y: i32);
-
}
-
-
// 类外实现方法
-
fn Button.Click[addr me: Self*](x: i32, y: i32) { ... }
-
fn Button.MoveTo[addr me: Self*](x: i32, y: i32) { ... }
-
-
class ImageButton extends Button {
-
...
-
}
abstrct class
就是抽象类,它不能被实例化,因为其中有抽象方法。抽象类与抽象方法的概念和Java类似。抽象方法等同于C++中的纯虚函数
。
base class
不仅是可以被继承(扩展)的类,还能实例化。因为它里面不会有抽象方法,所有继承而来的抽象方法都要被实现。base class
中也能用virtual
修饰成员函数,这个语法是从C++中的虚函数而来的。
泛型
泛型接口
定义泛型接口来做泛型代码的类型检查
-
interface Summary {
-
fn Summarize[me: Self]() -> String;
-
}
这个interface不是Java中的interface,而是有点像C++中的Concept,对泛型做类型检查用的。
泛型函数
-
fn PrintSummary[T:! Summary](x: T) {
-
Console.Print(x.Summarize());
-
}
定义了一个支持Summary泛型接口的泛型函数PrintSummary
实现泛型接口
-
class NewsArticle {
-
...
-
impl as Summary {
-
fn Summarize[me: Self]() -> String { ... }
-
}
-
}
所以可以使用泛型函数来调用实现了泛型接口的类对象
-
// n 是 NewsArticle类型
-
PrintSummary(n);
也可以直接调用
-
// n 是 NewsArticle类型
-
n.Summarize();
扩展其他包的API
-
import OtherPackage;
-
-
interface Summary {
-
fn Summarize[me: Self]() -> String;
-
}
-
-
// 泛型函数
-
fn PrintSummary[T:! Summary](x: T) {
-
Console.Print(x.Summarize());
-
}
-
-
// 扩展外部API的接口
-
external impl OtherPackege.Tweet as Summary {
-
fn Summarize[me: Self]() -> String { ... }
-
}
-
-
fn SummarizeTweet(t: Tweet) {
-
PrintSummary(t);
-
}
我们导入了一个外部的包OtherPackege,它之中有一个Tweet类型,然后我们可以通过external impl
来扩展它支持它本不存在的泛型接口。
指针
基本语法:
-
// 定义i32类型变量x,值为5
-
var x: i32 = 5;
-
// 把x的值改成10
-
x = 10;
-
// 定义i32*类型的指针p,指向变量x的地址
-
var p: i32* = &x;
-
// 通过指针修改x的值为7
-
*p = 7;
-
// 定义i32*类型的指针q,使用&*p,同样指向变量x的地址
-
var q: i32* = &*p;
-
// 通过指针q修改x的值为0
-
*q = 0;
-
// 定义一个i32类型的变量y,值为0
-
var y: i32 = *p;
另外Carbon的指针不支持空指针,如果想表示不存在,使用Optional。
与C++互操作
与C++互操作是Carbon宣传的重点,也是最大难点。现在Carbon语言还不完善,这里举一个Keynote中演示的例子。
有一个C++的头文件circle.h
-
struct Circle {
-
float r;
-
}
Carbon调用C++
然后编写一个Carbon代码文件:geometry.carbon
-
package Geometry api;
-
import Math;
-
-
import Cpp library "circle.h";
-
-
fn PrintArea(circles: Slice(Cpp.Circle)) {
-
var area: f32 = 0;
-
for (c: Cpp.Circle in circles) {
-
area += Math.Pi * c.r * c.r;
-
}
-
Print("Total area: {0}", area);
-
}
可以通过 import Cpp library "circle.h";
这种语法来引用C++头文件中声明的类型。
C++调用Carbon
写一个C++的源文件:
-
#include <vector>
-
#include "circle.h"
-
-
#include "geometry.carbon.h"
-
-
auto main(int argc, char* argv) -> int {
-
std::vector<Circle> circles = {{1.0}, {2.0}};
-
-
Geometry::PrintArea(circles);
-
return 0;
-
}
最后提醒
最后再次提醒,某些KeyNote中演示的Carbon语法仅仅是当前规划中的设计,但还没有被实现。当前已经实现,能够通过编译语法,参考Github仓库中explorer/testdata
目录中的代码。
文章来源: blog.csdn.net,作者:程序员编程指南,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_41055260/article/details/126066663
- 点赞
- 收藏
- 关注作者
评论(0)