質問

C++11 紹介します ユーザー定義リテラル これにより、既存のリテラルに基づいた新しいリテラル構文の導入が可能になります (int, hex, string, float) これにより、どの型でも文字通りのプレゼンテーションが可能になります。

例:

// imaginary numbers
std::complex<long double> operator "" _i(long double d) // cooked form
{ 
    return std::complex<long double>(0, d); 
}
auto val = 3.14_i; // val = complex<long double>(0, 3.14)

// binary values
int operator "" _B(const char*); // raw form
int answer = 101010_B; // answer = 42

// std::string
std::string operator "" _s(const char* str, size_t /*length*/) 
{ 
    return std::string(str); 
}

auto hi = "hello"_s + " world"; // + works, "hello"_s is a string not a pointer

// units
assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

一見とてもクールに見えますが、接尾辞を付けることを考えようとしたときに、実際にどの程度適用できるのか疑問に思います _AD そして _BC 日付の作成 オペレーターの順序が原因で問題があることがわかりました。 1974/01/06_AD まず評価するだろう 1974/01 (当たり前のように ints) そしてその後になって初めて、 06_AD (8 月と 9 月については言うまでもありませんが、 0 8 進数の理由のため)。これは、構文を次のようにすることで回避できます。 1974-1/6_AD そのため、演算子の評価順序は機能しますが、扱いにくいです。

私の質問は要約すると、この機能は正当化されると思いますか?ということです。C++ コードを読みやすくするために他にどのようなリテラルを定義したいですか?


2011 年 6 月の最終ドラフトに合わせて構文を更新

役に立ちましたか?

解決

コンストラクター呼び出しの代わりにユーザー定義リテラルを使用する利点がある場合を次に示します。

#include <bitset>
#include <iostream>

template<char... Bits>
  struct checkbits
  {
    static const bool valid = false;
  };

template<char High, char... Bits>
  struct checkbits<High, Bits...>
  {
    static const bool valid = (High == '0' || High == '1')
                   && checkbits<Bits...>::valid;
  };

template<char High>
  struct checkbits<High>
  {
    static const bool valid = (High == '0' || High == '1');
  };

template<char... Bits>
  inline constexpr std::bitset<sizeof...(Bits)>
  operator"" _bits() noexcept
  {
    static_assert(checkbits<Bits...>::valid, "invalid digit in binary string");
    return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
  }

int
main()
{
  auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits;
  std::cout << bits << std::endl;
  std::cout << "size = " << bits.size() << std::endl;
  std::cout << "count = " << bits.count() << std::endl;
  std::cout << "value = " << bits.to_ullong() << std::endl;

  //  This triggers the static_assert at compile time.
  auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits;

  //  This throws at run time.
  std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits");
}

利点は、実行時例外がコンパイル時エラーに変換されることです。 文字列を取得するビットセットアクターに静的アサートを追加できませんでした(少なくとも文字列テンプレート引数がない場合)。

他のヒント

一見すると、単純な糖衣構文のように見えます。

しかし、さらに深く見てみると、それは単に構文上の糖衣以上のものであることがわかります。 これは、C++ ユーザーのオプションを拡張して、個別の組み込み型とまったく同じように動作するユーザー定義型を作成します。 この小さな「ボーナス」は、C++ に対する C++11 の非常に興味深い追加です。

C++ では本当に必要でしょうか?

過去数年間に私が書いたコードにはほとんど使用されていませんが、C++ で使用しなかったからといって、それが C++ にとって面白くないというわけではありません。 別の C++ 開発者.

C++ (おそらく C でも) では、コンパイラ定義のリテラルを使用して、整数値を short またはlong integer として、実数を float または double (さらには long double) として、文字列を通常の文字またはワイド char として型指定していました。 。

C++ では、独自の型を作成することができました。 (すなわち、クラス)、オーバーヘッド (インライン化など) が発生しない可能性があります。演算子を型に追加して、同様の組み込み型のように動作させることができました。これにより、C++ 開発者は、言語自体に行列や複素数を追加した場合と同じように自然に行列や複素数を使用できるようになります。キャスト演算子を追加することもできます (これは通常は悪い考えですが、場合によってはそれが適切な解決策となることもあります)。

ユーザー型を組み込み型として動作させるには、まだ 1 つの点が欠けていました。ユーザー定義リテラル。

したがって、これは言語の自然な進化だと思いますが、できる限り完全にするために次のようにします。」型を作成し、それを可能な限り組み込み型として動作させたい場合は、次のツールを使用します。"

これは、ブール値、整数などを含むすべてのプリミティブを構造体にし、すべての構造体をオブジェクトから派生させるという .NET の決定に非常に似ていると思います。この決定だけでも、Java がその仕様にどれだけボックス化/アンボックス化ハックを追加したとしても、プリミティブを扱う場合、.NET は Java の範囲をはるかに超えています。

本当に C++ で必要ですか?

この質問は あなた 答える。ビャルネ・ストルストラップではない。ハーブサッターではありません。C++ 標準委員会のメンバーではありません。これが理由です C++ で選択できます, そして、有用な表記法を組み込み型のみに限定することはありません。

もし あなた 必要であれば、それは歓迎すべき追加です。もし あなた やめて、まあ...使用しないでください。費用はかかりません。

C++ へようこそ。機能はオプションです。

むくんだ???あなたのコンプレックスを見せてください!

肥大化と複雑さには違いがあります(冗談です)。

Niels が示したように、 ユーザー定義リテラルはどのような新機能を C++ に追加しますか?, 、複素数を記述できることは、C と C++ に「最近」追加された 2 つの機能のうちの 1 つです。

// C89:
MyComplex z1 = { 1, 2 } ;

// C99: You'll note I is a macro, which can lead
// to very interesting situations...
double complex z1 = 1 + 2*I;

// C++:
std::complex<double> z1(1, 2) ;

// C++11: You'll note that "i" won't ever bother
// you elsewhere
std::complex<double> z1 = 1 + 2_i ;

現在、C99 の「double complex」型と C++ の「std::complex」型の両方を、演算子のオーバーロードを使用して乗算、加算、減算などができるようになりました。

しかし、C99 では、組み込み型として別の型と、組み込み演算子のオーバーロードのサポートが追加されただけです。そして、別の組み込みリテラル機能も追加しました。

C++ では、彼らは言語の既存の機能を使用しただけであり、リテラル機能は言語の自然な進化であると考え、それを追加しました。

C では、別の型に対して同じ表記法の拡張が必要な​​場合、量子波動関数 (または 3D ポイント、または仕事の分野で使用している基本的な型) を追加するロビー活動が行われるまで、運が悪いです。組み込み型としての C 標準は成功します。

C++11 では、自分で行うことができます。

Point p = 25_x + 13_y + 3_z ; // 3D point

腫れていますか?いいえ, C と C++ の両方の複合体がリテラルの複合値を表す方法を必要とすることからわかるように、その必要性は存在します。

設計が間違っているのでしょうか?いいえ, 、他のすべての C++ 機能と同様に、拡張性を念頭に置いて設計されています。

表記のみを目的としているのでしょうか?いいえ, 、コードにタイプセーフを追加することもできるためです。

たとえば、CSS 指向のコードを想像してみましょう。

css::Font::Size p0 = 12_pt ;       // Ok
css::Font::Size p1 = 50_percent ;  // Ok
css::Font::Size p2 = 15_px ;       // Ok
css::Font::Size p3 = 10_em ;       // Ok
css::Font::Size p4 = 15 ;         // ERROR : Won't compile !

これにより、値の割り当てに強力な型指定を強制することが非常に簡単になります。

危険ですか?

良い質問。これらの関数には名前空間を付けることができますか?「はい」の場合は、ジャックポットです。

ともかく、 すべてのことと同様、ツールを不適切に使用すると自殺する可能性があります. 。Cは強力で、Cガンの使い方を間違えると頭を撃ち落とされる可能性があります。C++ には C ガンだけでなく、メス、テーザー銃、その他ツールキットに含まれるツールがすべて含まれています。メスの使い方を誤って出血して死ぬ可能性があります。または、非常にエレガントで堅牢なコードを構築することもできます。

他の C++ 機能と同様に、それは本当に必要なのでしょうか?これは、C++ で使用する前に答えなければならない質問です。そうしない場合、費用はかかりません。しかし、本当に必要な場合は、少なくとも言語があなたを失望させることはありません。

日付の例は?

あなたの間違いは、演算子を混合していることだと思われます。

1974/01/06AD
    ^  ^  ^

/ は演算子であるため、コンパイラはそれを解釈する必要があるため、これは避けられません。そして、私の知る限り、それは良いことです。

あなたの問題の解決策を見つけるには、別の方法でリテラルを書きます。例えば:

"1974-01-06"_AD ;   // ISO-like notation
"06/01/1974"_AD ;   // french-date-like notation
"jan 06 1974"_AD ;  // US-date-like notation
19740106_AD ;       // integer-date-like notation

個人的には、整数と ISO 日付を選択しますが、それはニーズによって異なります。これが、ユーザーが独自のリテラル名を定義できるようにすることの重要な点です。

数学的コードには非常に便利です。私の頭の中では、次の演算子の使用法がわかります。

度の場合は

deg。これにより、絶対角度の記述がはるかに直感的になります。

double operator ""_deg(long double d)
{ 
    // returns radians
    return d*M_PI/180; 
}

さまざまな固定小数点表現(DSPおよびグラフィックスの分野でまだ使用されている)にも使用できます。

int operator ""_fix(long double d)
{ 
    // returns d as a 1.15.16 fixed point number
    return (int)(d*65536.0f); 
}

これらは、使用方法の良い例のように見えます。コード内の定数を読みやすくするのに役立ちます。コードを読めなくする別のツールでもありますが、すでに多くのツールが悪用されているため、もう1つはそれほど害はありません。

UDLは名前空間です(宣言/ディレクティブを使用してインポートできますが、 3.14std :: i のようなリテラルの名前空間を明示的に指定することはできません)。大量の衝突。

実際にテンプレート化(およびconstexpr'd)できるという事実は、UDLを使用して非常に強力な機能を実行できることを意味します。 Bigintの作成者は、コンパイル時に(constexprまたはテンプレートを使用して)計算された任意の大きな定数を最終的に持つことができるため、非常に満足しています。

std :: string s のように、標準で(見た目から)いくつかの便利なリテラルが表示されないのは残念ですおよび虚数単位の i

UDLによって節約されるコーディング時間は実際にはそれほど長くはありませんが、読みやすさは大幅に向上し、より高速な実行のために、より多くの計算をコンパイル時間にシフトできます。

少しコンテキストを追加します。私たちの仕事には、ユーザー定義のリテラルが非常に必要です。 MDE(Model-Driven Engineering)に取り組んでいます。 C ++でモデルとメタモデルを定義します。実際にEcoreからC ++へのマッピングを実装しました( EMF4CPP )。

問題は、モデル要素をC ++のクラスとして定義できる場合に発生します。メタモデル(Ecore)を引数付きのテンプレートに変換するアプローチを取っています。テンプレートの引数は、タイプとクラスの構造特性です。たとえば、2つのint属性を持つクラスは次のようになります。

typedef ::ecore::Class< Attribute<int>, Attribute<int> > MyClass;

ただし、モデルまたはメタモデルのすべての要素には通常名前が付いています。書きたい:

typedef ::ecore::Class< "MyClass", Attribute< "x", int>, Attribute<"y", int> > MyClass;

BUT、C ++、C ++ 0xでは、テンプレートへの引数として文字列が禁止されているため、これは許可されません。名前を文字ごとに書くことができますが、これは明らかに混乱です。適切なユーザー定義リテラルを使用すると、同様のことを書くことができます。 &quot; _n&quot;を使用するとします。モデル要素名を識別するため(正確な構文を使用せず、単に考えを作るため):

typedef ::ecore::Class< MyClass_n, Attribute< x_n, int>, Attribute<y_n, int> > MyClass;

最後に、これらの定義をテンプレートとして使用すると、型情報、識別、変換などがコンパイル時にコンパイラによって決定されるため、モデル要素、モデル変換などをトラバースするための本当に効率的なアルゴリズムを設計できます時間。

Bjarne Stroustrupがこの CでUDLについて説明しています+11トーク、タイプリッチインターフェイスの最初のセクションで、20分前後。

UDLに対する彼の基本的な議論は、三段論法の形式をとります:

  1. &quot; Trivial&quot;型、つまり組み込みのプリミティブ型は、些細な型エラーのみをキャッチできます。より豊富な型を持つインターフェイスにより、型システムはより多くの種類のエラーをキャッチできます。

  2. 豊富に型付けされたコードがキャッチできる型エラーの種類は、実際のコードに影響を与えます。 (彼は重要な定数の寸法誤差のために悪名高い失敗した火星気候オービターの例を挙げています。)

  3. 実際のコードでは、ユニットはほとんど使用されません。リッチな型を作成するためにランタイムコンピューティングやメモリオーバーヘッドが発生することはコストがかかりすぎるため、人々はそれらを使用しません。 (経験的に、ライブラリは10年前から存在していましたが、誰も使用していません。)

  4. したがって、エンジニアが実際のコードでユニットを使用できるようにするには、(1)実行時のオーバーヘッドが発生せず、(2)表記上許容されるデバイスが必要でした。

必要な理由は、コンパイル時のディメンションチェックのサポートのみです。

auto force = 2_N; 
auto dx = 2_m; 
auto energy = force * dx; 

assert(energy == 4_J); 

例: PhysUnits-CT-Cpp11 、小さなC ++ 11、C ++ 14ヘッダー-コンパイル時の寸法分析と単位/数量の操作と変換のための専用ライブラリ。 Boost.Units よりシンプルで、ユニットシンボル m、g、s、 metricなどのリテラルm、k、Mなどの接頭辞は、標準のC ++ライブラリ、SIのみ、次元のべき乗にのみ依存します。

うーん...この機能についてはまだ考えていません。あなたのサンプルはよく考えられていて、確かに興味深いものです。 C ++は現在のように非常に強力ですが、残念ながら、読むコードの一部で使用される構文は非常に複雑です。読みやすさは、すべてではないにしても、少なくとも大部分です。そして、そのような機能は読みやすくするためのものです。最後の例を取り上げる場合

assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

...それを今日どのように表現しますか。 KGクラスとLBクラスがあり、暗黙的なオブジェクトを比較します。

assert(KG(1.0f) == LB(2.2f));

そしてそれも同様です。長い名前を持つ型や、アダプタを作成するためのこのような素晴らしいコンストラクタを持つことを望んでいない型がある場合、オンザフライの暗黙的なオブジェクトの作成と初期化のための素晴らしい追加になるかもしれません。一方、メソッドを使用してオブジェクトをすでに作成および初期化することもできます。

しかし、数学についてはニルスに同意します。たとえば、CおよびC ++の三角関数では、ラジアンでの入力が必要です。程度はあると思いますので、Nilsが投稿したような非常に短い暗黙の変換は非常に便利です。

最終的には、構文上の砂糖になりますが、読みやすさにはわずかな影響があります。そして、おそらくいくつかの式も書きやすいでしょう(sin(180.0deg)はsin(deg(180.0)よりも書きやすいです)。そして、コンセプトを乱用する人がいるでしょう。しかし、言語を乱用する人は使うべきですC ++のような表現力のあるものではなく、非常に制限の多い言語。

ああ、私の投稿は基本的に何も言っていません:それは大丈夫だろう、影響は大きすぎません。心配しないでください。 :-)

この機能を必要としたことは一度もありませんでしたが(これは Blub 効果かもしれません) 。私のひざの反応は、足が不自由だということであり、リモートで追加と解釈される可能性のある操作に対してoperator +をオーバーロードするのがクールだと思う人と同じ人にアピールする可能性があります。

C ++は通常、使用される構文について非常に厳格です-プリプロセッサを除いて、カスタム構文/文法を定義するために使用できるものはあまりありません。例えば。既存のオペラートをオーバーロードすることはできますが、新しいものを定義することはできません-IMOこれはC ++の精神と非常に調和しています。

よりカスタマイズされたソースコードを作成する方法は気にしませんが、選択した点は私にとって非常に孤立しているように見えます。

意図した使用でも、ソースコードの読み取りがはるかに困難になる可能性があります。単一の文字は、文脈からは決して特定できない広大な副作用を伴う場合があります。 u、l、fに対称性があるため、ほとんどの開発者は1文字を選択します。

これはスコープを問題に変える可能性もあり、グローバル名前空間で単一の文字を使用することはおそらく悪い習慣とみなされ、ライブラリを簡単に混在させると思われるツール(名前空間と記述識別子)はおそらくその目的を無効にします。

&quot; auto&quot;とブーストユニットですが、この追加に値するほどではありません。

しかし、どのような巧妙なアイデアを思いついたのでしょうか。

次のようなバイナリ文字列にユーザーリテラルを使用しました:

 "asd\0\0\0\1"_b

std :: string(str、n)コンストラクタを使用して、 \ 0 が文字列を半分にカットしないようにします。 (プロジェクトはさまざまなファイル形式で多くの作業を行います。)

これは、 std :: string を捨てて std :: vector のラッパーを選択したときにも役に立ちました。

その中のラインノイズは巨大です。また、読むのは恐ろしいです。

教えてください、彼らはそのような新しい構文の追加を何らかの種類の例で推論しましたか?たとえば、すでにC ++ 0xを使用しているプログラムがいくつかありますか?

私にとって、このパート:

auto val = 3.14_i

この部分を正当化しない:

std::complex<double> operator ""_i(long double d) // cooked form
{ 
    return std::complex(0, d);
}

他の1000行でもi-syntaxを使用しても同様です。書く場合、おそらくそれに沿って何か他の何かの10000行も書きます。特にあなたがおそらくほとんどどこにでもこれを書くだろうとき:

std::complex<double> val = 3.14i

'auto'-キーワードは正当化されるかもしれませんが、おそらくそうです。しかし、この側面ではC ++ 0xよりも優れているため、C ++のみを使用できます。

std::complex<double> val = std::complex(0, 3.14);

まるで…そんなに簡単。どこでも使用する場合、すべてのstdブラケットと先のとがったブラケットは単に足が不自由だとさえ思います。 std :: complexをcomplexに変換するためのC ++ 0xの構文を推測し始めません。

complex = std::complex<double>;

それはおそらく簡単なことですが、C ++ 0xではそれほど単純ではないと思います。

typedef std::complex<double> complex;

complex val = std::complex(0, 3.14);

おそらく? &gt; :)

とにかく、ポイントは:std :: complex(0、3.14)の代わりに3.14iを書くことです。いくつかの非常に特殊な場合を除いて、全体として多くの時間を節約できません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top