C++繼任者?Carbon語法速覽!
7月19日Cpp North大會上,谷歌的C++專家Chandler Carruth發(fā)表了『C++: What Comes Next? (Announcing the Carbon Language experiment) 』的主題演講,官宣了正在實驗中的Carbon語言,其目標是成為C++的繼任者。該消息迅速火爆全球,中文互聯(lián)網(wǎng)圈在7月20號也開始大量報道這一消息。

Chandler Carruth在谷歌負責LLVM編譯器的優(yōu)化,也是谷歌在 C++ 委員會的代表。
目前Carbon語言在Github上已經(jīng)開源,地址: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、類外定義函數(shù)等等。當然已經(jīng)實現(xiàn)的語法部分可能不穩(wěn)定,后期也可能會再有重大調(diào)整也未可知。
由于當下并且沒有支持Carbon語法的代碼高亮插件,所以下面代碼中的高亮并不準,敬請見諒。
Hello World
package ExplorerTest api;
第一行是定義了package(包),包名是ExplorerTest。后面的api,那個不是包名的一部分,但是又不能省略(當前可以是api或impl)。
fn Main() -> i32 {
var s: auto = "Hello world!";
Print(s);
return 0;
}
定義了main函數(shù),Carbon中使用的Main()。從這個Hello World的例子中,變量語法、函數(shù)定義語法可見一斑。
變量
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循環(huán)的語法。同樣也能發(fā)現(xiàn)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能比較的只能是整型、枚舉。
函數(shù)
fn Add(a: i64, b: i64) -> i64 {
return a + b;
}
Carbon中這個Add函數(shù)的寫法和Rust中實現(xiàn)一個Add幾乎一模一樣。比如聲明參數(shù)的時候類型在后,并且冒號分割的參數(shù)寫法。還有fn關鍵字。
當然在函數(shù)體內(nèi)邏輯復雜的時候,會和Rust不同,因為Rust還有不帶分號的表達式語法,表達式的值就是整個函數(shù)的值。Carbon沒有那種怪異的東西。
另外Carbon也能進行返回值的類型推導:
fn Add(a: i64, b: i64) -> auto {
return a + b;
}
一個更復雜的函數(shù)的例子:
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);
}
參數(shù)默認都是常量,除非聲明成指針類型。
面向?qū)ο?/span>
類與對象
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;
}
成員變量與成員函數(shù)
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);
}
// 類外定義成員函數(shù)
fn NewsAriticle.AsHtml[me: Self]() -> String{ ... }
Carbon中類中成員訪問控制權限默認都是public,如果需要聲明成私有則需要單獨加private關鍵字。這個行為和C/C++的struct相同,但是和主流語言的class都不同。
定義在類中的函數(shù),如果有[me: Self]表示是只讀的成員函數(shù),在函數(shù)中不能修改類對象的成員變量。me在函數(shù)體中是表示對當前對象的引用,類似C++的(*this)。
如果有[addr me: Self*]表示的是可對當前對象進行修改的函數(shù)。me在函數(shù)體中類似C++的this指針。
[me: Self]或[addr me: Self*]的成員函數(shù),也可以稱作方法(method),如果類中的函數(shù)沒有[me: Self]或[addr me: Self*],則表示是一個和對象無關的函數(shù),等價于C++中的static成員函數(shù)。這個設計很像python中的類中成員函數(shù)的設計。
繼承與抽象
Carbon只支持單繼承,這沒的說。值得注意的是普通的class關鍵字定義的類型默認都是final的,即不能被繼承生成子類(俗稱『絕育』)。但abstrct class和base class關鍵字定義的類型可以被繼承:
// 抽象類(abstract class)不能被實例化,因為其中可能包含抽象方法
abstract class UIWidget {
// 抽象方法(abstract fn)沒有實現(xiàn)
abstract fn Draw[me: Self](s: Screen);
abstract fn Click[addr me: Self*](x: i32, y: i32);
}
// base class 允許擴展和實例化
base class Button extends UIWidget {
// 實現(xiàn)抽象方法
impl fn Draw[me: Self](s: Screen) { ... }
impl fn Click[addr me: Self*];
// 新增了一個虛函數(shù)(virtual fn)
virtual fn MoveTo[addr me: Self*](x: i32, y: i32);
}
// 類外實現(xiàn)方法
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++中的純虛函數(shù)。
base class不僅是可以被繼承(擴展)的類,還能實例化。因為它里面不會有抽象方法,所有繼承而來的抽象方法都要被實現(xiàn)。base class中也能用virtual修飾成員函數(shù),這個語法是從C++中的虛函數(shù)而來的。
泛型
泛型接口
定義泛型接口來做泛型代碼的類型檢查
interface Summary {
fn Summarize[me: Self]() -> String;
}
這個interface不是Java中的interface,而是有點像C++中的Concept,對泛型做類型檢查用的。
泛型函數(shù)
fn PrintSummary[T:! Summary](x: T) {
Console.Print(x.Summarize());
}
定義了一個支持Summary泛型接口的泛型函數(shù)PrintSummary
實現(xiàn)泛型接口
class NewsArticle {
...
impl as Summary {
fn Summarize[me: Self]() -> String { ... }
}
}
所以可以使用泛型函數(shù)來調(diào)用實現(xiàn)了泛型接口的類對象
// n 是 NewsArticle類型
PrintSummary(n);
也可以直接調(diào)用
// n 是 NewsArticle類型
n.Summarize();
擴展其他包的API
import OtherPackage;
interface Summary {
fn Summarize[me: Self]() -> String;
}
// 泛型函數(shù)
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宣傳的重點,也是最大難點。現(xiàn)在Carbon語言還不完善,這里舉一個Keynote中演示的例子。
有一個C++的頭文件circle.h
struct Circle {
float r;
}
Carbon調(diào)用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++調(diào)用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語法僅僅是當前規(guī)劃中的設計,但還沒有被實現(xiàn)。當前已經(jīng)實現(xiàn),能夠通過編譯語法,參考Github倉庫中explorer/testdata目錄中的代碼。
