私はChannel9で Herb Sutterとのインタビュー を見てきました。彼はビデオの最後に、将来のC++標準のために左から右への言語構文が彼のウィッシュリストの一番上にあると述べました(ただし、彼はC++をそのように変更すると、まったく別の獣になることを認めています)。
の他に:
人間にとってより理解しやすく、肉眼でより明確;例えば.
//C syntax
/*pointer to function taking a pointer to function(which takes 2 integers as
arguments and returns an int), and an int as arguments and returning an int*/
int (*fp)(int (*ff)(int x, int y), int b)
//Go analogous syntax which is left to write
f func(func(int,int) int, int) int
解析が容易(ビデオで説明されているように、ツールのサポートが向上します-コードリファクタリングなど)
プログラミング言語の「左から右」構文には他にどのような利点がありますか。私はPascalとGoだけがそのような構文を採用していることを知っています(そして、Goはこれから理解するように完全には行きません ブログ投稿 私も例を取り上げました)実現可能でしょうか?そのような構文を持つシステムプログラミング言語?
基本的な利点は、解析がよりシンプルでユニークなことです。行が解析された後、コンパイラーは正確なタイプが何であるかを知っているので、それ以降、タイプがどのように定義されたかは無関係であることに注意してください。
配列型または関数ポインタ型の引数を返す関数は、現在のところ読みにくいです。
// common way of obtaining the static size in elements of an array:
template <typename T, int N>
char (&array_size_impl( T (&)[N] ))[N];
// alternative parser:
template <typename T, int N> // this would probably be changed also
array_simple_impl function( T [N] & ) char [N] &;
そして、誤解の可能性は低くなります(most-vexing-parseとして):
// Current C++
type x( another_type() ); // create an instance x of type passing a
// default constructed another_type temporary?
// or declare a function x that returns type and takes as argument
// a function that has no arguments and returns another_type
// How the compiler reads it:
x function( function() another_type ) type;
// What many people mean:
x type { another_type{} };
C++ 0x(つまり{}
で初期化を識別します)。左から右へのアプローチでは、定義する内容がより明確になることに注意してください。多くの人々(確かに私)は、過去にこの構文解析エラーによって(一度以上)いつか噛まれましたが、左から右への構文ではそうではありません。
関数ポイントを宣言するためのC構文は、使用法を反映することを目的としています。 <math.h>
からの次のような通常の関数宣言について考えてみます。
double round(double number);
ポイント変数を使用するには、タイプセーフティを使用してポイント変数を割り当てることができます
fp = round;
fp
ポイント変数を次のように宣言する必要があります。
double (*fp)(double number);
したがって、必要なのは、関数の使用方法を確認し、その関数の名前をポインター参照に置き換えて、round
を*fp
にすることだけです。ただし、括弧を追加する必要があります。これは、多少面倒になると言う人もいます。
おそらく、これは以前は関数シグネチャすらなかった元のCでより簡単でしたが、ここに戻らないでください。
特に厄介なのは、引数として受け取るか、関数へのポインタを返す関数、またはその両方を宣言する方法を理解することです。
関数がある場合:
void myhandler(int signo);
これをシグナル関数(3)に次のように渡すことができます:
signal(SIGHUP, myhandler);
または、古いハンドラーを保持したい場合は、
old_handler = signal(SIGHUP, new_handler);
とても簡単です。非常に簡単なこと、または非常に簡単でもないことは、宣言を正しくすることです。
signal(int signo, ???)
さて、関数宣言に戻り、名前をポイント参照に置き換えます。
signal(int sendsig, void (*hisfunc)(int gotsig));
gotsig
を宣言していないため、省略した場合は読みやすくなります。
signal(int sendsig, void (*hisfunc)(int));
またはそうでないかもしれません。 :(
次のように、signal(3)も古いハンドラーを返すため、それだけでは不十分です。
old_handler = signal(SIGHUP, new_handler);
したがって、これらすべてを宣言する方法を理解する必要があります。
void (*old_handler)(int gotsig);
割り当てる変数には十分ですここでは実際にgotsig
を宣言しているのではなく、old_handler
のみを宣言していることに注意してください。これで本当に十分です:
void (*old_handler)(int);
これにより、signal(3)の正しい定義が得られます。
void (*signal(int signo, void (*handler)(int)))(int);
この時までに、それが混乱であることに誰もが同意すると思います。抽象化に名前を付ける方が良い場合もあります。しばしば、本当に。適切なtypedef
を使用すると、これははるかに理解しやすくなります。
typedef void (*sig_t) (int);
これであなた自身のハンドラー変数は
sig_t old_handler, new_handler;
そしてsignal(3)の宣言はちょうど
sig_t signal(int signo, sig_t handler);
いきなり理解できる。 *を取り除くと、紛らわしい括弧の一部も取り除かれます(そして、括弧は常に物事を作ると言います簡単理解する—ええと!)。使い方は同じです:
old_handler = signal(SIGHUP, new_handler);
しかし、今では、old_handler
、new_handler
、およびsignal
の宣言に初めて遭遇したとき、またはそれらを記述する必要があるときに、それらの宣言を理解するチャンスがあります。
非常に参考資料を参照することなく、これらのことの正しい宣言を自分で考案できるCプログラマーはほとんどいません。
私が知っていたのは、カーネルとデバイスドライバーの仕事をしている人たちへのインタビューの質問で、この質問がかつてあったからです。 :)もちろん、候補者がクラッシュしてホワイトボードに焼き付いたため、そのようにして多くの候補者を失いました。しかし、この分野での経験はあるが実際には仕事ができないと主張する人々を雇うことも避けました。
ただし、この広範囲にわたる困難さのために、これを使用するためだけに平均より3シグマ上に座っているトリプルアルファオタクプログラマである必要がなくなったすべての宣言に取り掛かる方法を持つことは、おそらく賢明であるだけでなく実際に合理的です一種の快適なもの。
左から右に注目すると、少しポイントを逃したと思います。
CとC++の問題は、それらが持つ恐ろしい文法です。これは、人間(人間)と解析(ツール)の両方を解釈することが困難です。
より多くのconsistent(またはregular)文法を使用すると、両方でより簡単になります。また、解析が容易なことは、ツールが簡単であることを意味します。ほとんどの現在のツールは、C++を正しく機能させません。
だから、おそらくあなたはそれを読んで解析することに焦点を当てているときにそれを釘付けにした...そしてそれは大したことだ:)