C++ 热点问题一席谈(一)

      Bjarne Stroustrup 2005 新春专访

荣耀 访 荣耀/刘未鹏 译

荣耀: Herb Sutter 和Stan Lippman 目前正在微软主持C++/CLI 的设计工作,意图将动态的、基于组件的.NET 编程模型和ISO C++ 集成在一起。您对此有何评价?您认为C++ 需要.NET 吗?您认为C++/CLI 会取得成功吗?

Bjarne: 不,C++ 根本不需要.NET ,C++ 只需要最小限度的运行时支持,用于new/delete 、异常处理以及RTTI 等,而且仅当你使用这些特性时才需要。C++ 程序通常可以使用每一分可用的资源,在硬件上直接跑。C++ 的这些能力使其非常适合于系统级编程以及嵌入式系统任务。当然,也有些C++ 应用需要.NET ,比如那些为了和微软.NET 框架和服务紧密集成而专门设计的应用。然而,C++ 语言和标准库的宗旨是远离这些平台相关性的纠缠。另一方面,许多.NET 设施都依赖于C++ ,因为除了C++ 之外,再也找不到更通用、更高效的语言来很好地完成这个任务,从这个意义上说,.NET 需要C++ 。

从“很多人将会使用它”这个意义上来说,C++/CLI 是会成功的。使用.NET CLI ,开发者选择甚少,而C++ 则是最佳选择之一,而且很明显在Windows 上也是,因为微软给予C++ 最好的支持。话虽如此,我仍然倾向于在设计系统时保持良好的移植性,而将对平台相关或专有特性的使用限制在特定的代码块中,并使用以ISO 标准C++ 所表达的接口去访问它们。

荣耀: 尽管我现在相信这是一个毫无意义的问题,不过我想我最好还是澄清一下。当我说“C++ 需要.NET 吗?”,我的意思是想问“我们需要.NET 来使C++ 更普及吗?”。这就好比问“世界和平需要美国吗?”,或者,“我们需要美国来维护世界和平吗?”。当然了,我们都不喜欢讨论政治性话题,也许这个比方很不合适。

Bjarne: 政治关乎可行性。从这个意义上来说,我们必须考虑政治,而你的问题当然也是合理的。鉴于微软在软件领域的地位以及它对.NET 强大而完全的支持(将.NET 的系统接口以CLI 来表达),.NET 的地位会变得很重要。要想在微软的世界里玩得转的话,C++ 必须很好地绑定到.NET 。事实上这种绑定(C++/CLI )已经建立了,微软还为之申请了ECMA 标准。.NET 跟我理想中的尚有些差距,而C++/CLI 如果让我来设计的话可能也不会是这个样子,然而不可否认的是,C++/CLI 在.NET 上的确是能力非常强的语言,也是迄今为止为.NET 设计的语言中能力最强的。如果微软未考虑将C++ 作为.NET 上的关键语言之一,或者.NET 平台上的应用创建者没有坚持对C++ 提供第一流支持的话,情况会糟很多。

所以,为了能够在微软的世界里流行,C++ 需要一个良好的CLI 绑定。我仍然鼓励人们将C++/CLI 仅仅视作一个绑定物。这就是说,把对C++/CLI 特性的使用隔离到一些特定的区域里,并且通过ISO 标准C++ 设施去访问它们。C++/CLI 的一些设施使其成为一门具有吸引力的语言,然而和标准C++ 却相去甚远,所以如果你在代码中到处使用这些C++/CLI 设施的话,那么你将失去平台无关性,并有损失性能优势的潜在危险。

荣耀: 鉴于Herb Sutter 的双重身份:ISO C++ 标准委员会主席和微软软件架构师,C++/CLI 对C++0x 标准会产生什么样的影响?这种可能的影响是您希望看到的吗?

Bjarne: C++/CLI 会在一些领域对C++ 产生影响,因为将会有许多人使用它。显而易见,在使用中人们将会建议加入一些新的设施,以便和ISO C++ 平滑地互操作。在确保ISO C++ 平台中立的前提下,这些建议会依据其优点而被评估。我并不认为Herb 的双重身份会带来任何负面影响。请注意,ISO 委员会的会议召集人的角色属于管理方面的。我认为微软将Herb 的才能贡献到标准化进程中是一个积极的信号。一如往常,微软在实践着其“拥抱并扩展”策略,但至少他们拥抱的是ISO C++ 而非某种C++ 方言。

荣耀: C++0x 标准大概可于哪一年颁布?目前标准化工作进展如何?我们在这个新标准中预期可以看到哪些新特性?

Bjarne: 我希望三、四年内能够颁布,不过目前我们还没有一个明确的进度表。

我们打算在语言的扩展上持保守态度,并在与C++98 的兼容性方面仔细斟酌。改进的关键可能会落在对泛型编程更好的支持以及对新手更易学习上。我们期望一个关于模板实参的类型系统“concepts ”能成为泛型编程的基石。

在标准库的扩展方面我们打算胆子更大一些。新标准库技术报告也许会使你对它的发展方向有一些认识。我们的关键目标是使标准库成为一个更广泛的系统编程平台!

荣耀: 一个冒昧的问题。为什么C++ 标准委员会主席是Herb Sutter 而不是您?我记得您是进化工作组主席,我们都很有兴趣知道您在目前标准化过程中具体从事什么工作。

Bjarne: 我并不想担当会议召集人的职务。Herb 和 他的前任们在那个职位上比我所能做到的要出色得多。会议召集人主要负责管理性和组织性的事务。我的(非正式的)角色在于努力维持语言一致性的方向。我是语 言进化工作组的主席,这个工作组负责处理所有语言扩展方面的提议。这个职位意味着绝大部分定义语言改变的文本都是我写出来的,而这些文本最终形成了标准文 档。

作为进化工作组的主席,目前我正致力于三件事情:“concept ”,改进的初始化设施,以及对C++ 新手的更好支持。你可以从我的主页上以下链接看到人们对标准下一个修订版(即“C++0x ”)建议的期望特性列表:http://www.research.att.com/~bs/C++.html 。 简单的数一下你就会发现这些特性根本不能全部塞到标准中去,所以,我们需要一些优先级上的考虑。此外,我们还需要把注意力集中到一些相互关的问题上,而不 是每次单独处理这些个体提议。如果把每个特性都单独考虑的话,你无论如何也不能得到一个内在一致且易教学的语言。正如语言本身一样,特性必须是为解决问题 而设计的,而不仅仅是一个一个地“看上去很美”。

让我扼要介绍一下我眼下正以高优先级进行的三个语言扩展:concepts ,初始化,以及“消除一些令人不愉快的瑕疵”:

在《C++ 语言的设计与演化》(D&E )中对模板的讨论部分,我花了整整三页来讨论模板实参的约束。很明显,我觉得它需要一个更好的解决方案。在使用模板(例如标准库算法)时哪怕出一丁点儿差错都可能招致极其“壮观”而无用的错误信息。问题在于,模板代码对其模板实参的“期望”是隐式的。让我们考虑一下find_if() :

template < class In, class Pred>
In find_if(In first, In last, Pred pred)
{
    while (first!=last && !pred(*first)) ++first;
    return first;
}

这里,我们对类型In 和Predicate 作了若干假设。从代码中我们可以看出,In 必须支持!= 、* 和++ ,并且这些操作符还必须具有恰当的语义。另外,我们必须能够拷贝In 类型的对象作为实参和返回值。类似地还可以看出,我们可以“以* 作用在In 对象上所返回的值”作为实参来调用Pred ,并且把“! ”运用到该调用返回的结果上,从而得到一个可以在语义上看成是布尔值的东西。然而,这些约束在代码中都是隐式表达的。标准库为前向迭代器(此处为In )以及谓词(此处为Pred )小心翼翼地记录了各自要求的条件,但编译器可不会阅读文档!试试以下错误的代码,看看你的编译器会有什么反应:

find_if(1,5,3.14); // 错误!

我以前的想法提供了一个不完备、但相当高效的解决方案,即使用一个构造函数来检查对于模板实参的假定条件(见D&E 15.4.2 ),这个解决方案现在得到了广泛运用,被称为“concepts (概念)”或“constraints classes (约束类)”。你可以从我的主页上的技术FAQ 中找到一些例子:http://www.research.att.com/~bs/bs_faq2.html#constraints

然而,我们真正想要告诉编译器的是“我们期望模板实参是什么样子的”或者“我们期望模板实参满足哪些要求”,例如:

template < Forward_iterator In, Predicate Pred>
In find_if(In first, In last, Pred pred);

假设我们可以表达Forward_iterator 和Predicate 是什么,编译器就能够在不用查看find_if() 定义的情况下检查对它的调用是否正确。这里我们所要做的就是为模板实参构建一个类型系统。在现代C++ 中,这种“类型的类型”被称为“concepts ”。有多种方式可以用于表达concepts ,眼下我们暂且把它们看成一些受到直接的语言支持并具有优雅语法的“约束类”。一个concept 表明了一个类型必须提供哪些能力,但并不强制规定它们如何提供这些能力。理想的concept (例如< Forward_iterator In> )应该非常类似于数学抽象(对于任意的In ,它必须可被递增(++ )、解引用(* )以及拷贝),就像原来的形式“< class T> ”从数学上来说是“针对所有的类型T ”那样。

这样一来,在仅仅给出find_if() 的声明的情况下,我们可以写:

int x = find_if(1,2,Less_than< int> (7));

这将会失败,因为int 并不支持解引用(* )。换句话说,这个调用将不能通过编译,因为int 并不是一个Forward_iterator 。很重要的一点是,这将会使编译器更容易在该调用首次被看到的那一点上报告用户所犯的错误。

遗憾的是,仅仅知道iterator 实参是Forward_iterator 以及predicate 实参是Predicate 还不足以保证对find_if() 的调用能够成功编译。这两个实参类型之间是有着交互作用的。说得详细一点就是,predicate 所接受的实参是一个被解引用的iterator (pred(*first) )。我们的目标在于对模板进行“和调用相分离”的完全检查以及无需查看模板定义就能对每次调用进行的完全检查,所以,concept 必须具有足够强的表达力,以便处理这种模板实参之间的交互关系。方式之一是对concept 本身也进行参数化,就像模板本身的参数化那样。例如:

template< Value_type T,
Forward_iterator< T> In, //
对一个T 序列进行迭代
Predicate< bool,T> Pred> //
接受一个T 并返回一个bool
In find_if(In first, In last, Pred pred);

在这儿,我们要求Forward_iterator 必须指向一个T 类型的元素,而该元素的类型必须和Predicate 的实参类型一样(译注:实际上只要类型兼容即可)

这方面的工作正在进行中。你可以从C++ 委员会的文件、学术文献以及有关C++0x 的讨论中找到这方面更多的信息,在这里我没有太多的时间或地方进行更详细地阐述。“concept ”的目标是提供模板使用和模板定义的完美的分离式检查,同时不引入任何运行期负担(译注:在C# 的所谓的泛型中,concept 只不过是间接函数调用的语法糖而已,运行期额外负担仍然存在。 )以及不必要的concept 耦合(译注:在C# 所谓的泛型中,concept 要求模板实参继承自一个公共基类,因此耦合仍然存在。)

换句话说,我们想要把静态类型检查的好处引入到C++ 中的高度抽象的层面上去,同时不损及目前的模板技术所提供的灵活性和效率。

C++ 的基本思想之一是“对用户定义类型提供和内建类型一样良好的支持”(见D&E4.4 )。但考虑下面这个例子:

double vd[ ] = { 1.2, 2.3, 3.4, 4.5, 5.6 };
vector< double> v(vd, vd+5);

我们可以直接使用“初始化列表”来直接初始化数组,而对于vector ,最好的情况是我们可以先创建一个内建数组然后再用它来初始化vector 。如果只有很少的几个初始化值,我可能会倾向于使用push_back() 以避免将初始值的数目显式“写死”在代码中(上面例子中的初值是5 个):

vector< double> v;
v.push_back(1.2);
v.push_back(2.3);
v.push_back(3.4);
v.push_back(4.5);
v.push_back(5.6);

我想任何人都不会认为这两种解决方案有任何“优雅”可言。要想得到可维护性更好的代码并且让vector 比内建(具有固有的危险性)数组更“讨人喜欢”的话,我们需要这样的能力:

vector< double> v = { 1.2, 2.3, 3.4, 4.5, 5.6 };

或者:

vector< double> v ({ 1.2, 2.3, 3.4, 4.5, 5.6 });

由于实参传递是依据初始化来定义的,因此这对接受vector 为参数的函数同样奏效:

void f(const vector< double> & r);
// …

f({ 1.2, 2.3, 3.4, 4.5, 5.6 });

(译注:这里即是说,实参传递和初始化的语义是一样的,例如:

void f(T a);
f(x);

这里“把x 作为实参传递给a ”的过程等同于

T a = x;

这是个初始化表达式。)

我相信这种初始化器(initializers )的一般形式将会成为C++0x 的一部分,这不过是将成为对构造函数进行全面检修的一部分,因为人们已经发现了有关构造函数的不少弱点,这些弱点看起来可以通过对构造函数进行一些修整来解决,例如“转发构造函数(forwarding constructor )”、“有保障的编译期构造函数(guaranteed compile-time constructors )”以及“继承的构造函数(inherited constructors )”等。

第三件事是“剔除语言里的一些令人不愉快的瑕疵”,也就是说,修整一些细小的不合常规或不方便的东西,它们对有经验的C++ 老手不会产生什么影响,然而却可能严重打击C++ 新手。一个非常简单的例子就是:

vector< vector< double>> v;

在C++98 中,这里有一个语法错误,因为“>> ”被看成一个单独的词汇标记,而不是两个“> ”。正确的写法如下:

vector< vector< double> > v;

对于C++98 这样一个“不近人情”的规则,虽然有足够技术上的理由,但这不应该强加给任何背景的新手(包括其它语言的专家)。如果编译器不接受前一种最为明显的v 的声明形式的话,那么C++ 用户和教师都会在这上面浪费大量的时间。我希望这个“>> 问题”以及其它一些瑕疵都会在C++0x 中消失。事实上,在和Francis Glassborow 以及其他一些人的工作中,我一直努力去系统地消除出现频率最高的此类语言瑕疵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值