我最近正在学习D,并且开始对某种语言有所了解。我知道它提供了什么,我还不知道如何使用所有东西,并且我对D习语等不了解很多,但是我正在学习。
我喜欢D。这是一种不错的语言,以某种方式对C进行了巨大的更新,并且做得很好。似乎没有一个功能可以“实现”,但是实际上经过了深思熟虑和精心设计。
您经常会听到D是C ++应该是什么(我问这个问题是为了避免不必要的火焰大战,每个人都需要自己下定决心)。我还从几位C ++程序员那里听说,他们比D更喜欢D。
我自己,虽然我了解C,但我不能说我了解C ++。如果有人认为C ++比D作为语言要好,那么我想听听他们都知道C ++和D的东西(这不是通常的“它具有更多的第三方库”或“有更多的资源”或“
D由一些非常熟练的C ++程序员(在D社区的帮助下,Walter Bright和Andrei Alexandrescu设计)来解决C ++的许多问题拥有,但是到底有没有真正变得更好的东西?他错过了什么吗?您认为不是更好的解决方案吗?
还请注意,我所说的是D 2.0,而不是D 1.0。
#1 楼
C ++优于D的大多数事情都是元数据:C ++具有更好的编译器,更好的工具,更成熟的库,更多的绑定,更多的专家,更多的教程等。从根本上讲,它拥有越来越多的外部事物。会期望使用更成熟的语言。这是无可争议的。就语言本身而言,我认为C ++在某些方面比D更好。可能还有更多,但我可以列出其中的一些:
C ++可以更好地考虑类型系统
其中的类型系统存在很多问题目前,D,这似乎是设计中的疏忽。例如,由于const的可传递性以及postblit构造函数在值类型上的工作方式,如果该struct包含类对象引用或指针,则当前无法将const struct复制到非const结构。安德烈(Andrei)说他知道如何解决这个问题,但是没有透露任何细节。这个问题当然是可以解决的(引入C ++样式的复制构造函数将是一个解决方法),但这是当前语言上的一个主要问题。
困扰我的另一个问题是缺少逻辑const(即没有
mutable
像在C ++)。这对于编写线程安全代码非常有用,但是很难(不可能?)在const对象中进行惰性初始化(想像一个const'get'函数,该函数在第一次调用时构造并缓存返回的值)。最后,鉴于这些现有问题,我担心类型系统的其余部分(
pure
,shared
等)一旦投入使用,将如何与该语言中的其他所有内容进行交互。标准库(Phobos)目前很少使用D的高级类型系统,因此我认为在压力下是否可以承受这个问题是合理的。我对此表示怀疑,但很乐观。请注意,C ++有一些类型系统疣(例如,非传递性const,需要
iterator
和const_iterator
),这使其很难看,但是C ++的类型系统在某些方面有一些错误,但这并不能阻止您完成工作像D有时所做的那样。编辑:要澄清一下,我相信C ++具有更好的思想类型系统-不一定是更好的类型系统-如果可以的话。从本质上讲,在DI中使用C ++中没有的类型系统的所有方面都存在风险。
D有时太方便了
有人批评您经常听到的C ++是它对您隐藏了一些底层问题,例如诸如
a = b;
之类的简单赋值可能会执行许多操作,例如调用转换运算符,调用重载赋值运算符等,这在代码中很难看到。有些人喜欢,有些人不喜欢。无论哪种方式,在D中,由于opDispatch
,@property
,opApply
,lazy
之类的东西都可能会变糟(更好?),它们有可能将无辜的代码变为您不希望看到的东西。I不要以为就个人而言这不是一个大问题,但是有些人可能会觉得不合时宜。
D需要进行垃圾收集
这可能会引起争议,因为可以运行D没有GC。但是,仅仅因为有可能并不意味着它是实用的。没有GC,您将失去很多D的功能,使用标准库就像走在雷区(谁知道哪个函数分配内存?)。就我个人而言,我认为在没有GC的情况下使用D是完全不切实际的,而且如果您不喜欢GC(例如我),那么这可能会令人反感。
朴素的数组定义在D中分配内存
这是我的宠儿:
int[3] a = [1, 2, 3]; // in D, this allocates then copies
int a[3] = {1, 2, 3}; // in C++, this doesn't allocate
显然,要避免在D中分配内存,必须执行以下操作:
static const int[3] staticA = [1, 2, 3]; // in data segment
int[3] a = staticA; // non-allocating copy
这些小小的“背后隐藏”分配是我前面两点的好例子。
编辑:请注意,这是一个正在解决的已知问题。编辑:现已修复。没有分配发生。
结论
我一直专注于D vs C ++的缺点,因为这就是所要提出的问题,但是请不要将此帖子视为C ++是我可以轻松地在D比C ++更好的地方发表更多文章。由您决定使用哪个。
评论
几年前(2.0之前),我看了看D。那时实际上并不需要垃圾收集-默认情况下存在垃圾收集,但是您可以选择不使用低级代码。我认为这是错误的,因为我找不到办法选择退出。例如,在基于树的容器中,库代码可以管理树节点本身的内存。整个树仍然可以被收集,使用IIRC的析构函数收集所有这些节点。但是该容器中数据引用的对象也应该是可收集的-应该有一个钩子来标记树中GC的所有数据项。
–Steve314
2011年8月1日下午13:46
您仍然可以为低级代码禁用GC-彼得说,目前的语言在很大程度上取决于它。另外,您可以告诉GC使用其API:来自core.memory的GC.addRange扫描其托管堆之外的范围。
–弗拉基米尔·潘捷列夫(Vladimir Panteleev)
2011年8月2日,0:48
+1指出D标准库是垃圾回收,并且GC-off代码不是无缝互操作。这不是我考虑过的事情,但这似乎是克服的主要障碍。
–石匠
13年8月16日在13:38
#2 楼
当我加入D开发时,我身为最了解C ++的人之一。现在,我处于更加特殊的位置,成为最了解D的人之一。我并不是在说这是对适当的信誉或吹牛的权利,而只是说我很好奇有利位置来回答这个问题。同样,沃尔特也是如此。总的来说,问C ++(我的意思是C ++ 2011)比D做得更好,就像一个问题一样:“如果你付出专业打扫您的房屋,他们将在比以前更脏的地方?” C ++可以做到D不能做到的任何价值,对我和Walter而言,它始终像是一个痛苦的拇指,因此从定义上讲,几乎C ++永远无法做到D不能达到的目标。
在语言设计中很少理解的一件事(因为很少有人能真正做到一些运气)是非强制错误的数量比可能出现的要少得多。我们中的许多语言用户都在看某个或另一个结构,然后说:“噢!这太不对了!他们在想什么?”事实是,语言中最尴尬的事例是一些基本决定的结果,这些基本决定都是合理和可取的,但在根本上是相互竞争或矛盾的(例如,模块化和效率,简洁性和控制性等)。
牢记所有这些,我将列举一些我能想到的事情,并且我将逐一解释D的选择是如何从满足其他更高层次的宪章的愿望中衍生出来的。 />
D假定所有对象均可按位复制。这使得C ++的设计少之又少,特别是那些使用内部指针的设计,即内部包含指针的类。 (任何这样的设计都可以免费转换为D或将效率转换为D,但是会涉及翻译工作。)我们做出这一决定是为了大大简化语言,使对象复制更加有效,而无需用户干预或只需很少的干预,并避免整个复制结构的烂摊子和右值引用都具有相同的功能。
D禁止使用含糊不清的性别类型(无法确定它们是值类型还是引用类型)。此类设计在C ++中被一致回避,并且几乎总是错误的,但是其中一些在技术上是正确的。我们之所以选择该选项,是因为它不允许大多数错误代码,并且只有很小一部分可以重新设计的正确代码。我们认为这是一个很好的权衡。
D禁止多根层次结构。以前的发帖人对这个特定主题感到非常兴奋,但这是经过深造的基础,无根层次结构相对于具有相同根源的层次结构没有明显优势。
在D中,您不能抛出例如一个int。您必须抛出继承Throwable的对象。毫无疑问,D中的事务状态会更好,但是C ++可以做到D不能做到这一点。
在C ++中,封装的单元是一个类。在D中,它是一个模块(即文件)。 Walter做出此决定的原因有两个:将封装自然地映射到文件系统保护语义,以及消除对“朋友”的需要。这个选择很好地集成在D的整体模块化设计中。可以将事情更改为更像C ++,但这将迫使事情发生。 C ++的封装范围选择仅对C ++的物理设计有用。
可能有一两个较小的东西,但总体来说应该是这样。
评论
@DeadMG:要在C ++中工作,要移动的对象将需要一个指向该对象的后向指针(以便它可以在复制构造期间更新)。在这种情况下,在D中,您可以使用postblit构造函数来更新指针。如果您仅对D有了解,请不要与D争辩。
– Peter Alexander
2011年7月30日15:35
@Peter:即使生命周期严格基于范围,也应该作为参考?我应该浪费动态分配它的开销以及间接,缓存和收集的开销,因为我想给它加上别名?另外,我希望收集器可以确定性地收集它,以获得等效的语义。显然,这是不受控制的。
– DeadMG
11年7月30日在17:07
@sbi:顶级人士的存在根本不会影响您的选择。在类类型的晶格中,始终存在顶部和底部。底部仅在几种语言中是显式的。顶部(即对象等)在更多语言中是显式的。这些类型在概念上始终存在。当它们也可以访问时,它们只是为语言的用户提供了一些额外的功能而不会造成麻烦。
– Andrei Alexandrescu
2011年7月31日,1:15
@quant_dev:您会很高兴听到已经有一个状态良好的GSoC项目,该项目专注于使用BLAS的高性能线性代数。它还提供用于测试和基准测试目的的适当基元的“原始”实现。为了回答您的第二个问题,Java将比较数字库的标准设置得很低。它总是要克服访问高性能代数库的JNI障碍,并且语法会很糟糕,因为Java缺少运算符重载。
– Andrei Alexandrescu
2011年8月1日15:00
@PeterAlexander:DeadMG恰到好处。 “您不应该使用指向值类型的指针”显然忽略了以下事实:任何语言中的指针通常都与值类型一起使用(您是否真的希望看到Object *与int *一样广泛使用?)并且D似乎完全忽略了性能损失,或者声称它不存在。这显然是错误的-在许多情况下,高速缓存未命中非常明显,因此C ++始终比D具有更大的灵活性优势。
–user541686
2011年12月3日在8:32
#3 楼
我认为您将很难在D中找到很多东西,而这在客观上要比C ++差。您可以客观地说出更糟的D问题大多数是实施质量问题(这通常是由于语言和实施还很年轻,并且已经以惊人的速度被解决),或者是问题缺少第三方库(随时间推移而来)。语言本身通常比C ++更好,而C ++作为一种语言更好的情况通常是要么权衡取舍,C ++沿一种方式前进,D沿另一种方式前进,或者有人出于某种主观原因认为一个比另一个更好。但是,作为一种语言,C ++更好的直接客观原因的数量可能很少。实际上,我必须动脑筋想出为什么C ++的原因。作为一种语言,它比D更好。通常我想到的是权衡问题。
因为D的const是可传递的,并且由于该语言具有不变性,所以它具有更强大的功能保证比C ++的
const
更好,这意味着D没有也没有mutable
。它不能具有逻辑常量。因此,使用D的const系统会获得巨大收益,但是在某些情况下,您将无法像在C ++中那样使用const
。D仅具有一个强制转换运算符,而C ++具有4(如果您选择计算C强制转换运算符)。在通常情况下,这使处理D中的强制转换更为容易,但是当您实际上想要
const_cast
及其弟兄提供的额外复杂性/好处时,这是有问题的。但是D实际上足够强大,您可以使用模板来实现C ++的强制类型转换,因此,如果您真的想要它们,则可以使用它们(它们有时甚至可以最终出现在D的标准库中)。与C ++相比,D的隐式转换要少得多,并且更可能声明两个函数相互冲突(迫使您更确切地说明要使用的函数-通过转换或提供完整的模块路径) )。有时,这可能很烦人,但是它可以防止各种功能劫持问题。您知道您确实在调用要使用的函数。
D的模块系统比C ++的#includes更干净(更不用说,编译速度更快),但是它在模块之外缺少任何命名空间他们自己。因此,如果要在模块中使用命名空间,则必须走Java路线,并在类或结构上使用静态函数。它可以工作,但是如果您真的想使用命名空间,则显然不如真正的命名空间干净。但是,在大多数情况下,模块本身为您提供的命名空间就足够了(在涉及诸如冲突之类的东西时相当复杂)。
与Java和C#一样,D具有单一继承而不是多重继承,但是与Java和C#,它为您提供了一些奇妙的方法来获得相同的效果,而不会遇到C ++的多重继承所带来的所有问题(并且C ++的多重继承有时会变得很混乱)。 D不仅具有接口,而且具有字符串混合器,模板混合器和别名。因此,最终结果可以说是功能更强大,并且没有C ++的多重继承所具有的所有问题。
与C#相似,D分隔结构和类。类是具有继承性的引用类型,并且是从
Object
派生的,而结构是没有继承性的值类型。这种分离可以是好的,也可以是坏的。它摆脱了C ++中的经典切片问题,并帮助将真正是值类型的类型与应该是多态的类型分开,但是起初,至少,这种区别可能会使C ++程序员感到讨厌。最终,它有很多好处,但这确实迫使您处理类型有所不同。类的成员函数默认情况下是多态的。您不能将它们声明为非虚拟的。由编译器决定它们是否可以(这只有在它们是最终的并且没有从基类覆盖函数的情况下才是实际情况)。因此,在某些情况下这可能是性能问题。但是,如果您真的不需要多态性,那么您所要做的就是使用结构,这不是问题。
D有一个内置的垃圾收集器。许多来自C ++的人都认为这是一个严重的缺点,并且说实话,目前,其实现可能会花费一些认真的工作。它一直在改进,但是绝对不能与Java的垃圾收集器相提并论。但是,这可以通过两个因素来缓解。第一,如果您主要在堆栈上使用结构和其他数据类型,那么这不是一个大问题。如果您的程序不是经常在堆上分配和取消分配东西,那就没问题了。第二,如果愿意,可以跳过垃圾收集器,而只需使用C的
malloc
和free
即可。您必须避免或小心使用某些语言功能(例如数组切片),并且某些标准库在没有至少使用GC(尤其是字符串处理)的情况下才真正可用。如果确实愿意,可以在不使用垃圾收集器的情况下用D编写。明智的做法可能是先使用它,然后在分析表明它对性能关键代码造成问题的情况下避免使用它,但是如果需要,可以完全避免使用它。随着时间的流逝,GC的实施质量将得到改善,从而消除了使用GC可能引起的许多担忧。因此,最终,GC不会成为大问题,并且与Java不同,如果需要,可以避免使用它。可能还会有其他人,但这是我目前能想到的。而且,如果您会注意到,它们都是折衷方案。 D选择做一些不同于C ++的事情,这些事情在C ++的工作方式上具有明显的优势,但也有一些缺点。哪个更好取决于您在做什么,并且在很多情况下,一开始看起来似乎只会更糟,然后一旦您习惯了就不会有问题了。如果有的话,D中的问题通常是新问题,这是由其他语言以前没有做过或没有像D那样做过的新东西引起的。总体而言,D从C ++的错误中学到了很多。
D作为一种语言,以多种方式对C ++进行了改进,以至于我认为通常在客观上D会更好。 >
D有条件编译。这是我用C ++编程时经常会错过的功能之一。如果将C ++添加进去,那么C ++在涉及模板之类的东西上将得到长足的进步。
D具有编译时的反映。
变量默认是线程本地的,但是如果您使用,则可以是
shared
希望他们成为。这使得处理线程要比使用C ++干净得多。您完全可以控制。您可以使用immutable
和消息传递在线程之间进行通信,或者可以使变量shared
并使用互斥体和条件变量以C ++的方式进行。通过引入sync(类似于C#和Java),即使在C ++上,它也得到了改进。因此,D的线程情况远胜于C ++。D的模板比C ++的模板功能强大得多,从而使您可以做得更多,更轻松。加上模板约束,错误消息比C ++中的错误消息要好得多。 D使模板非常强大且可用。 D语言的主要合作者之一是Modern C ++ Design的作者,这绝不是偶然的。我发现与D模板相比,C ++模板严重缺乏,有时使用C ++进行编程时会感到非常沮丧。
D具有内置的合同编程。
D具有内置的单元测试框架。D具有对
string
(UTF-8),wstring
(UTF-16)和dstring
(UTF-32)的Unicode的内置支持。它使处理unicode变得容易。而且,如果您只想使用string
而通常不必担心unicode,则可以-尽管对unicode基础有所了解确实对某些标准库函数有所帮助。D的运算符重载比C ++更好,允许您可以使用一个函数同时重载多个运算符。一个典型的例子是当您需要重载基本算术运算符,并且除运算符外,它们的实现是相同的。字符串mixin使操作变得轻而易举,使您可以为它们全部提供一个简单的函数定义。
D的数组比C ++的数组好得多。它们不仅是具有长度的适当类型,而且可以附加并调整大小。连接它们很容易。最棒的是,它们有切片。这对于有效的阵列处理是一个巨大的福音。字符串是D中的字符数组,这不成问题(实际上很棒!),因为D的数组是如此强大。
我可以继续下去。 D所提供的许多改进都是小事(例如,使用
this
作为构造函数名称,或者不允许if语句或循环体使用分号作为整体),但是其中一些改进很大,将它们全部加在一起时,它提供了更好的编程体验。 C ++ 0x确实添加了D所缺少的C ++的某些功能(例如auto
和lambdas),但是即使进行了所有改进,从客观上讲,C ++作为一种语言也要比D更好。 。毫无疑问,有很多主观原因让彼此喜欢,而且D的实现相对不成熟有时可能会成为问题(尽管最近它一直在迅速改善-尤其是由于将存储库移至github),并且缺少第3方库绝对是一个问题(尽管D可以轻松调用C函数-在较小程度上是C ++函数-确实可以缓解问题)。但是,这些是实现问题的质量,而不是语言本身的问题。而且随着实现问题的质量固定下来,使用D将变得更加令人愉悦。
因此,我想对此问题的简短回答是“很少”。 D作为一种语言,通常优于C ++。
评论
垃圾收集语言使用的内存比非GC语言多2-5倍(根据Alexandrescu关于YT的说法),因此如果(内存使用)成为瓶颈,那肯定是一个问题。
– NoSenseEtAl
2011年8月19日在9:10
#4 楼
RAII和堆栈内存的使用情况D 2.0不允许RAII在堆栈上发生,因为它删除了在堆栈上分配类实例时使用
scope
关键字的值。您不能进行value-type D中的继承,如此有效地迫使您为任何形式的RAII进行堆分配。
,除非您使用
emplace
,但是使用起来非常痛苦,因为您必须手动分配内存。 (我尚未发现在D中使用emplace
是可行的。)#5 楼
C ++可以使您变得冗长得多。根据您喜欢推理还是冗长,这在您的眼中可能是好是坏。比较C ++中的运行时备注:
template <typename ReturnType, typename... Args>
function<ReturnType (Args...)> memoize(function<ReturnType (Args...)> func)
{
map<tuple<Args...>, ReturnType> cache;
return ([=](Args... args) mutable {
tuple<Args...> t(args...);
return cache.find(t) == cache.end()
? cache[t] : cache[t] = func(args...);
});
}
D中有相同的内容:
auto memoize(F)(F func)
{
alias ParameterTypeTuple!F Args;
ReturnType!F[Tuple!Args] cache;
return (Args args)
{
auto key = tuple(args);
return key in cache ? cache[key] : (cache[key] = func(args));
};
}
例如,请注意,
template <typename ReturnType, typename... Args>
与(F)
,Args...
与Args
,args...
与args
的额外冗长,等等。< br不论好坏,C ++都比较冗长。当然,您也可以在D中执行此操作:
template memoize(Return, Args...)
{
Return delegate(Args) memoize(Return delegate(Args) func)
{
Return[Tuple!Args] cache;
return delegate(Args args)
{
auto key = tuple(args);
return key in cache ? cache[key] : (cache[key] = func(args));
};
}
}
和它们看起来几乎相同,但是这需要
delegate
,而原始对象接受任何可调用的对象。 (C ++ 0x版本需要一个std::function
对象,因此无论哪种方式,它的输入都更加冗长和严格...如果您喜欢冗长,可能会很好,如果您不喜欢它,那么就很糟糕。)#6 楼
我对D不太了解,但是我认识的许多C ++程序员对此非常不满意,我个人必须同意-我不喜欢D的外观,也不会更亲近D。为了理解为什么D没有获得更多的吸引力,您需要首先了解什么会吸引人们使用C ++。总之,首要原因是控制。当您使用C ++编程时,便可以完全控制您的程序。是否要替换标准库?您可以。是否想进行不安全的指针转换?您可以。想违反const正确性吗?您可以。是否要更换内存分配器?您可以。是否想在原始内存中复制而不考虑其类型?如果您真的想要。是否要继承多个实现?这是你的葬礼。地狱,您甚至可以获得垃圾收集库,例如Boehm收集器。然后,您将遇到诸如性能之类的问题,这些问题与控制息息相关-程序员拥有的控制权越多,他制作程序的优化就越多。性能是继续使用C ++的主要原因之一。
在做一些研究并与几个尝试过C ++的人交谈时,我发现了以下几点:
统一的类型层次结构。 C ++用户很少使用继承,大多数C ++程序员更喜欢使用组合,并且只有在有充分理由的情况下,才应通过继承链接类型。对象的概念通过链接每种类型都严重违反了这一原理。另外,它违反了C ++最基本的原则之一-您仅使用所需的内容。就让程序员控制自己的程序而言,没有选择从Object继承的选择以及随之而来的成本与C ++所代表的语言非常强烈地冲突。
我听说过函数和委托的问题。显然,D同时具有函数和委托作为运行时可调用函数类型,它们并不相同,但是它们可以互换或...?我的朋友与他们有很多问题。这绝对是C ++的降级版本,它只有
std::function
,您已经完成。那么您就有了兼容性。 D与C ++并不特别兼容。我的意思是,没有语言与C ++兼容,让我们面对现实吧,除了C ++ / CLI有点作弊,但作为入门的障碍,必须要提到。
然后,有一些其他事情。例如,只需阅读Wikipedia条目。
import std.metastrings;
pragma(msg, Format!("7! = %s", fact_7));
pragma(msg, Format!("9! = %s", fact_9));
printf
是有史以来设计的最不安全的功能之一,与诸如gets
这样的大问题属于同一家族。旧的C标准库。如果您在Stack Overflow上搜索它,将会发现许多与滥用有关的问题。从根本上讲,printf
违反了DRY-您在格式字符串中提供类型,然后在为它提供参数时再次提供它。违反DRY的地方,如果您弄错了,就会发生非常糟糕的事情-例如,如果将typedef从16位整数更改为32位整数。它也根本无法扩展-想象如果每个人都发明了自己的格式说明符会发生什么。 C ++的iostream可能很慢,它们对运算符的选择可能不是最大的,它们的接口可以使用工作,但是从根本上保证了它们的安全性,并且不违反DRY,并且可以轻松扩展它们。这不是printf
可以说的。没有多重继承。那不是C ++的方式。 C ++程序员期望对他们的程序拥有完全的控制权,而强制执行您不能继承的语言违反了该原则。另外,它使继承(甚至更多)变得脆弱,因为如果由于要提供默认实现或某些东西而将类型从接口更改为类,突然间所有用户代码都将被破坏。那不是一件好事。
另一个例子是
string
和wstring
。在C ++中,必须在它们之间进行转换已经很痛苦,并且该库是否支持Unicode,并且这个旧的C库仅使用const char*
,并且必须根据所需的字符串参数类型编写同一函数的不同版本。值得注意的是,例如Windows标头具有一些非常令人讨厌的宏,以解决通常可能会干扰您自己的代码的问题。将dstring
添加到混合中只会使情况变得更糟,因为现在必须管理三种而不是两种字符串类型。具有多个字符串类型将增加维护难度,并引入重复的代码来处理字符串。Scott Meyers写道:
D是一种内置的编程语言帮助程序员应对现代软件开发的挑战。它通过促进通过精确接口互连的模块,紧密集成的编程范例联合,语言强制的线程隔离,模块化类型安全性,有效的内存模型等来实现。
语言增强的线程隔离不是一个加号。 C ++程序员希望完全控制自己的程序,而强制执行某种操作的语言绝对不是医生所要求的。
我还将提到编译时字符串操作。 D可以在编译时解释D代码。这不是一个加号。考虑一下由C相对有限的预处理器(所有资深C ++程序员都熟知)引起的巨大麻烦,然后想象一下此功能将被滥用的严重程度。在编译时创建D代码的能力很棒,但是它应该是语义的,而不是语法的。
此外,您可以期望有一定的反映。 D具有垃圾回收,C ++程序员将垃圾回收与Java和C#之类的语言相关联,而Java和C#在哲学上与之直接相对立,并且语法上的相似性也使他们想到。这不一定客观上是合理的,但一定要注意。
从根本上讲,它并没有提供C ++程序员无法做到的很多事情。用D编写阶乘元程序也许更容易,但是我们已经可以用C ++编写阶乘元程序。也许在D中您可以编写一个编译时的光线跟踪器,但是没有人真的想这样做。与基本违反C ++原理的行为相比,在D中可以做的事情并不是特别值得注意。
即使这些只是表面上的问题,我也很确定从表面上看,D实际上根本不像C ++,这可能是许多C ++程序员不向D迁移的一个好理由。也许D需要做得更好做广告。
评论
@DeadMG:这是100%不正确的,并且缺少要说的重点这绝对是C ++的降级,C ++只有std :: function,您就完成了。为什么?例如,因为您还有函数指针。在D中完全相同:D的“函数”是函数指针,D的“委托”与C ++的std :: function相同(除了它们是内置的)。任何地方都没有“降级”-它们之间是1:1对应的,因此,如果您熟悉C ++,就不会感到困惑。
–user541686
2011年8月1日12:43
@Mark Trapp:我必须承认,我不太理解您在该主题上的立场–评论不用于评论答案吗?
– dnadlinger
2011年8月1日13:26
@Mark Trapp:我的意思是,这里的大多数评论不是过时的(您链接的元讨论专门适用于已经包含在原始帖子中的建议),但指出了该帖子中仍然存在的事实不准确之处。
– dnadlinger
2011年8月1日19:26
关于格式的注释:D的格式函数是类型安全的(解决了安全/溢出问题),并且不违反DRY,因为格式字符串仅指定应如何格式化参数,而不是参数的类型。这要归功于D的类型安全变量。因此,该反对意见至少是完全无效的。
–贾斯汀W
2011年8月1日在22:05
@Mark:无论当前关于它们的政策是什么,我都觉得这很愚蠢,并且阻碍了评论讨论的删除。我认为这个答案进行了广泛的讨论(我现在对此很感兴趣),但是我不确定,而且我无法找出答案。您链接到的那个房间有超过1万条消息,而且我根本没有机会找到我似乎记得曾经进行过的讨论,但是却记不清其中的内容。关于此答案的讨论属于此处,属于此答案,而不属于某个聊天室,在聊天室中,他们可能混杂在有关性,毒品和摇滚乐的讨论中。
–sbi
2011年8月25日13:00
#7 楼
我在C ++中欣赏的一件事是能够将函数参数或返回值记录为C ++引用(而不是指针),从而暗示采用非null
值。D版本:
class A { int i; }
int foo(A a) {
return a.i; // Will crash if a is null
}
int main() {
A bar = null;
// Do something, forgetting to set bar in all
// branches until finally ending up at:
return foo(bar);
}
C ++版本:
class A { int i; };
int foo(A& a) {
return a.i; // Will probably not crash since
// C++ references are less likely
// to be null.
}
int main() {
A* bar = null;
// Do something, forgetting to set bar in all
// branches until finally ending up at:
// Hm.. I have to dereference the bar-pointer
// here, otherwise it wont compile. Lets add
// a check for null before.
if (bar)
return foo(*bar);
return 0;
}
为了公平起见,通过将
A
变成D,可以非常接近C ++。并将struct
参数标记为foo()
(类是D中的引用类型,而结构是D中的值类型,类似于C#)。我相信有一个计划为类创建一个
ref
模板D标准库构造代替。即便如此,与NonNullable
相比,我更喜欢Type&
的简洁性,并且更喜欢将non-nullable作为默认值(呈现类似NonNullable(Type)
和Type
的东西)。但是现在为D进行更改为时已晚,我现在正偏离主题。评论
D中的函数参数和返回值都可以用ref标记,以提供与C ++的&相同的效果。一个主要的区别是ref即使是const也不会是临时的。
–乔纳森·M·戴维斯(Jonathan M Davis)
2011年8月1日在1:29
我喜欢D中禁止返回对局部变量的引用的方式,直到我读完您的评论,我才意识到这一点。但是您仍然可以返回非局部null引用,而无需考虑C取消引用运算符会让您思考的方式。
–流明
2011年8月2日在20:57
你让事情变得混乱。类始终是引用,并且与ref分开。 D中的引用类似于Java中的引用。它们是托管指针。 ref传递或返回就像在C ++中通过&传递或返回。通过ref传递类引用就像在C ++中使用&传递指针(例如A *&)。类不会进入堆栈。是的,NonNullable将使得有一个可以保证为非空的类引用成为可能,但这与ref完全分开。您尝试在C ++代码中执行的操作在D中不起作用,因为类不在堆栈中。结构做。
–乔纳森·M·戴维斯(Jonathan M Davis)
2011年8月2日在22:51
所以是的,能够有一个非nullull的类引用会很好,但是C ++设法完成了您要显示的操作,因为它允许将类放在堆栈中并允许将指针取消引用。虽然可以在D中取消引用指针,但类是引用,而不是指针,因此您不能取消引用它们。由于您不能将它们放在堆栈上,也不能取消引用它们,因此D内置没有办法拥有不能为null的类。这是一种损失,但是NonNullable可以解决它,并且从结构和类的分离中获得的收益通常都更大。
–乔纳森·M·戴维斯(Jonathan M Davis)
2011年8月2日在23:13
按照语言标准,C ++引用不能为空(说“可能不会为空”是不正确的,因为它不能为空)。我确实希望有一种方法可以禁止null作为类的有效值。
–jsternberg
2011年8月3日在22:01
#8 楼
C ++比D做得更好的最重要的事情是与旧版库的接口。各种3D引擎,OpenCL等。随着D的出现,D具有更少的不同库可供选择。C ++和D的另一个重要区别是C ++具有多个财务独立的供应商,但截至2014年,D实际上只有一个人,一个由2人组成的团队来创建它。有趣的是,“第二个源代码原则”说项目永远不应该依赖于只有一个供应商的技术,组件,甚至对于软件来说也是如此。
为了比较,第一个版本的Ruby解释器是由Ruby的发明者Yukihiro Matsumoto编写的,但是2014时代的主流Ruby解释器实际上是由其他人从头开始编写的。因此,可以将Ruby视为一种拥有多个财务独立供应商的语言。另一方面,D可能是一项了不起的技术,但这取决于开发该技术的少数开发人员。
Java的历史表明,即使技术(在这种情况下为Java)具有好的,但单一的,有钱的人,不管公司的用户群如何庞大,都有很大的风险将该技术从本质上抛弃。引用了Apache Software Foundation,其中EC代表“执行委员会”:
Oracle向EC提供了自相矛盾的Java SE 7规范请求和许可证,严格限制了独立发行版的发行。规范的实现,最重要的是,禁止分发规范的独立开源实现。
作为历史记录,可以说Java小程序在HTML5 WebGL开发之前已有硬件加速了3D画布。如果Java是社区项目,则可以解决Java applet的启动速度问题,但是Java的唯一资助者Sun Microsystems的高管认为修复Java实现的重要性并不足够。最终结果:多个供应商提供的HTML5画布作为Java GUI框架(Swing等)的“穷人副本”。有趣的是,在服务器端,Python编程语言具有Java承诺的相同优势:只要不将Python应用程序编译为机器代码,就可以编写一次,在每台服务器上运行,无论硬件如何。 Python与Java一样古老/年轻,但是与Java不同,它由一个以上独立资助的开发人员团队(PyPy和主流Python解释器)支持。
摘要:
在评估用于生产的技术时,技术的最重要属性是人员,开发人员,社会过程,开发地点以及财务上独立的开发团队的数量。
使用技术T1优于技术T2是否更有利,取决于技术的供应商,以及技术T1是否比T2便宜地解决与项目相关的问题。例如,如果忽略了单个供应商问题,那么对于信息系统而言,Java将是比C ++更好的技术,因为Java二进制文件在部署到新硬件时不需要重新编译,并且无需进行与内存管理相关的软件开发工作Java比C ++小。从头开始开发的项目,例如不依赖其他库的项目,用D进行开发可能比使用C ++便宜,但是,另一方面,C ++有多个供应商,因此从长远来看风险较小。 (在Java示例中,Sun Microsystems几乎可以使用,但是Oracle实际上使Java成为“新COBOL”。)
一些C ++局限性的可能解决方法
克服C ++某些局限性的一种可能的解决方法是使用设计模式,其中将初始任务分为多个部分,然后对各个部分进行“排序”(分类,聚类,划分,其他-nice-words-for相同的东西)分为2类:与控制逻辑相关的任务,其中内存访问模式不允许局部性;类似信号处理的任务,可以轻松实现本地化。信号处理“类似”任务可以实现为一组相对简单的控制台程序。与控制逻辑相关的任务全部放置在一个用Ruby或Python或其他方式编写的脚本中,其中软件开发舒适性优先于速度。
为了避免昂贵的操作系统进程初始化和操作系统进程之间的数据复制,小型C ++控制台应用程序集可以实现为单个C ++程序,由Ruby / Python / etc作为“ servlet”启动。脚本。 Ruby / Python / etc。脚本在退出前关闭servlet。 “ servlet”和Ruby / Python / etc之间的通信。脚本是通过Linux命名管道或类似机制进行的。
如果最初的任务不容易将其划分为可以分为上述2个类的片断,那么可以尝试一下可能是重新表达该问题,以便更改初始任务。
评论
我确保D社区确实看到了这一点,因为我确信C ++开发人员比D开发人员多得多。这样,您将获得更多有趣或至少是各种各样的答案。同样,D2由Walter Bright设计,但也由Alexandrescu设计。您可能想在问题中解决此问题。
@Klaim:在(现在仍然)有很多社区参与D和标准库。
@Anto作为一种语言,C ++在使您(程序员)讨厌您的生活方面比D要好得多。
@jokoon:的确,是的,只需要很少的工作:digitalmars.com/d/2.0/interfaceToC.html