C ++の隠された機能?[閉まっている]


114

一連の質問の「隠された機能」に関しては、C ++の愛はありませんか?私はそれをそこに捨てるだろうと考えました。C ++の隠された機能は何ですか?


@Devtron-機能として販売されているいくつかの素晴らしいバグ(つまり、予期しない動作)を見ました。実際、ゲーム業界は実際にこれを今日実現しようとし、それを「緊急ゲームプレイ」と呼んでいます(また、Psi-Opsから「TK Surfing」をチェックしてください、純粋にバグだったので、そのままにしておきました。ゲームIMHOの最高の機能)
Grant Peters

5
@Laith J:786ページのISO C ++標準を最初から最後まで読んだ人はそれほど多くはありません-しかし、私はあなたがそれを持っていると思いますし、あなたはそれをすべて保持していると思いませんか?
j_random_hacker 2010

2
@Laith、@j_random:私の質問を参照してください。「プログラマのジョークは何を、どのように私はそれを認識し、適切な応答何であるか」でstackoverflow.com/questions/1/you-have-been-link-rolledが

回答:


308

ほとんどのC ++プログラマーは、3項演算子に精通しています。

x = (y < 0) ? 10 : 20;

ただし、左辺値として使用できることを理解していません。

(a == 0 ? a : b) = 1;

これは略記です

if (a == 0)
    a = 1;
else
    b = 1;

注意して使用してください:-)


11
とても興味深い。けれども、いくつかの判読不能なコードを作成していることがわかります。
Jason Baker、

112
うわぁ。(a == 0?a:b)=(y <0?10:20);
Jasper Bekkers、2009年

52
(b?trueCount:falseCount)++
Pavel Radzivilovsky

12
DunnoはGCC固有のものですが、これも機能していることに驚きました(value ? function1 : function2)()
Chris Burt-Brown、

3
@クリスバート・ブラウン:いいえ、彼らは同じ型(すなわちなし不履行引数)を持っている場合はどこでも動作するはずfunction1function2implictly関数ポインタに変換され、結果が暗黙的に変換されたバックです。
MSalters 2010年

238

エラーなしでURIをC ++ソースに入れることができます。例えば:

void foo() {
    http://stackoverflow.com/
    int bar = 4;

    ...
}

41
しかし、関数ごとに1つだけだと思いますか?:)
コンスタンティン

51
@jpoh:httpの後にコロンが続くと、「goto」ステートメントで後で使用する「ラベル」になります。上記の例のgotoステートメントでは使用されていないため、コンパイラから警告が表示されます。
utku_karatas 2008年

9
プロトコルが異なる限り、複数追加できます!ftp.microsoft.com gopher://aerv.nl/1など...
Daniel Earwicker 2009年

4
@Pavel:コロンが後に続く識別子はラベルです(gotoC ++にはあるで使用するため)。2つのスラッシュに続くものはすべてコメントです。したがって、とはhttp://stackoverflow.comhttp(あなたは理論的に書くことができるラベルであるgoto http;)、そして//stackoverflow.comちょうど行末コメントです。これらは両方とも正当なC ++であるため、構造はコンパイルされます。もちろん、それは漠然と有用なことは何もしません。
David Thornley、2010年

8
残念ながらgoto http;実際にはURLをフォローしていません。:(
Yakov Galka

140

ポインター演算。

C ++プログラマーは、発生する可能性のあるバグのため、ポインターを避けることを好みます。

私が今まで見た中で最もクールなC ++?アナログリテラル。


11
バグのためポインタを避けていますか?ポインターは、基本的には動的C ++コーディングのすべてです。
Nick Bedford

1
アナログリテラルは、難読化されたC ++コンテストエントリ、特にASCIIアートタイプに最適です。
Synetech、2011

119

私はそこのほとんどの投稿に同意します。C++はマルチパラダイム言語であるため、見つけることができる「隠された」機能(「未定義の動作」以外は避けなければならない)は、機能の巧妙な使用法です。

これらの機能のほとんどは、言語の組み込み機能ではなく、ライブラリベースの機能です。

最も重要なのはRAIIであり、Cの世界から来たC ++開発者によって何年もの間無視されてきました。オペレーターの過負荷は、配列のような動作(添字演算子)、ポインターのような演算(スマートポインター)、組み込みのような演算(行列の乗算)の両方を可能にする誤解されている機能であることがよくあります。

例外の使用はしばしば困難ですが、いくつかの作業により、例外の安全性を通じて非常に堅牢なコードを生成できます仕様(失敗しない、または成功する、または戻るようなコミットのような機能を持つコードを含む)ますその元の状態)。

C ++の「隠された」機能で最も有名なのは、テンプレートのメタプログラミングです。です。これにより、ランタイムではなくコンパイル時にプログラムを部分的(または完全に)実行できます。ただし、これは難しい作業であり、試す前にテンプレートをしっかりと把握しておく必要があります。

他にも、複数のパラダイムを利用して、C ++の祖先、つまりCの外部に「プログラミングの方法」を作成します。

ファンクタを使用することで、型安全性が追加され、ステートフルになる関数をシミュレートできます。コマンドパターンを使用すると、コードの実行を遅らせることができます。他のほとんどの設計パターンは、C ++に簡単かつ効率的に実装して、「公式のC ++パラダイム」のリストに含まれていないはずの代替コーディングスタイルを生成できます。

テンプレートを使用することで、最初は考えていなかったタイプを含め、ほとんどのタイプで機能するコードを作成できます。タイプセーフを増やすこともできます(自動化されたタイプセーフなmalloc / realloc / freeなど)。C ++オブジェクト機能は非常に強力です(したがって、不注意に使用すると危険です)が、動的なポリモーフィズムでさえ、C ++の静的バージョンであるCRTPがあります。ます。

スコットマイヤーズの「Effective C ++」タイプの本またはHerb Sutterの「Exceptional C ++」タイプの本のほとんどは読みやすく、C ++の既知の機能とあまり知られていない機能に関する情報の宝物であることがわかりました。

私が好むのは、あらゆるJavaプログラマーの頭を恐ろしくさせるものです。C++では、オブジェクトに機能を追加するための最もオブジェクト指向の方法は、メンバーではなくメンバー以外の非フレンド関数を使用する方法です。関数(つまり、クラスメソッド)。

  • C ++では、クラスのインターフェイスは、同じ名前空間内のそのメンバー関数と非メンバー関数の両方です

  • フレンド以外の非メンバー関数には、内部クラスへの特権アクセスがありません。そのため、非メンバーで非フレンドのメンバー関数を使用すると、クラスのカプセル化が弱まります。

これは、経験豊富な開発者でさえ驚くことはありません。

(出典:とりわけ、ハーブサッターのオンライングルオブザウィーク#84:http : //www.gotw.ca/gotw/084.htm


+1非常に徹底した答え。明らかな理由で不完全です(そうしないと、「隠された機能」はもうありません!):p回答の最後の最初の点で、クラスインターフェイスのメンバーについて言及しました。「..はメンバー関数であり、フレンドは非メンバー関数である」という意味ですか?
wilhelmtell 2008年


1について言及しているのは、ケーニッヒルックアップであるに違いありません。
Özgür

1
@wilhelmtell:いいえ、違います... :-p ...私は「そのメンバー関数と非フレンド非メンバー関数」を意味します。 「外」は、シンボルの検索で機能します
paercebal 2008年

7
素晴らしい投稿です。特に最後の部分では+1になり、ほとんどの人が気づいていません。おそらく、Boostライブラリも「隠し機能」として追加するでしょう。私はそれをC ++が持っていたはずの標準ライブラリと考えています。;)
2008年

118

学校でずっと聞いたことがなかったために、少し隠されていると思われる言語機能の1つは、名前空間のエイリアスです。ブーストのドキュメントでその例に出くわすまで、それは私の注意を引くことはありませんでした。もちろん、私はそれについて知っているので、標準のC ++リファレンスで見つけることができます。

namespace fs = boost::filesystem;

fs::path myPath( strPath, fs::native );

1
これは、使用したくない場合に役立つと思いますusing
Siqi Lin 2010

4
これは、スレッドセーフでない、または2対1対バージョンと言うのスレッドセーフを選択するかどうか、また、実装間のスイッチへの道として有用だ
トニー・デルロイ

3
大規模な名前空間階層を持つ非常に大規模なプロジェクトで作業していて、ヘッダーによって名前空間が汚染されないようにしたい(そして、変数宣言を人間が読めるようにしたい)場合に特に便利です。
Brandon Bohrer、2011年

102

変数は、forループのinit部分で宣言できるだけでなく、クラスや関数でも宣言できます。

for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) {
    ...
}

これにより、異なるタイプの複数の変数が可能になります。


31
あなたがそれを行うことができることを知ってうれしいですが、個人的には私は本当にそのようなことをしないようにしようと思います。主に読みにくいからです。
Zoomulator、2009

2
実際、このコンテキストで機能するのはペアを使用することです:for(std :: pair <int、float> loop = std :: make_pair(1,2); loop.first> 0; loop.second + = 1)
Valentinハイニッツ

2
@Valentinでは、隠し機能を無効にするのではなく、VS2008に対してバグレポートを作成することをお勧めします。それは明らかにコンパイラのせいです。
Johannes Schaub-litb 2010年

2
うーん、msvc10でも機能しません。なんて悲しい:(
avakar

2
@avakar実際には、gccはv4.6でもそれを拒否するバグを導入しました:) gcc.gnu.org/bugzilla/show_bug.cgi?id=46791を
Johannes Schaub-litb

77

配列演算子は結合的です。

A [8]は*(A + 8)の同義語です。加算は結合的であるため、*(8 + A)と書き換えることができます。これは、..... 8 [A]の同義語です。

役に立たなかった... :-)


15
実際、このトリックを使用するときは、使用しているタイプに注意を払う必要があります。A [8]は実際には8番目のAですが、8 [A]はアドレス8から始まるA番目の整数です。Aがバイトの場合、バグがあります。
ヴィンセントロバート

38
あなたはあなたが「連想的」と言う「可換性」を意味しますか?
DarenW 2008

28
ヴィンセント、あなたは間違っている。のタイプAはまったく関係ありません。たとえば、Aでしたchar*、コードはまだ有効でしょう。
Konrad Rudolph、

11
Aはポインターでなければならず、クラスオーバーロードの演算子[]ではないことに注意してください。
デビッドロドリゲス-ドリベス

15
Vincent、これには1つの整数型と1つのポインター型が必要であり、CもC ++もどちらが先に行くかを気にしません。
David Thornley、

73

あまり知られていないことの1つは、共用体もテンプレートになる可能性があることです。

template<typename From, typename To>
union union_cast {
    From from;
    To   to;

    union_cast(From from)
        :from(from) { }

    To getTo() const { return to; }
};

また、コンストラクターやメンバー関数を持つこともできます。継承とは関係ありません(仮想関数を含む)。


面白い!では、すべてのメンバーを初期化する必要がありますか?それは通常の構造体の順序に従っていますか?つまり、最後のメンバーが以前のメンバーの「上に」初期化されることを意味しますか?
j_random_hacker 2009年

j_random_hackerああ、それはナンセンスです。良いキャッチ。それは構造体になるので、私はそれを書きました。待って、修正します
Johannes Schaub-litb

これは未定義の動作を引き起こしませんか?
Greg Bacon

7
@gbacon、はい、それに応じて設定および使用された場合From、未定義の動作を呼び出しToます。このような共用体は、定義された動作で使用できます(Tounsigned charの配列、またはと初期シーケンスを共有する構造体From)。未定義の方法で使用しても、低レベルの作業には役立つ場合があります。とにかく、これはユニオンテンプレートの1つの例にすぎません。テンプレート化されたユニオンには他の用途があるかもしれません。
ヨハネスシャウブ-litb 2010年

3
コンストラクタに注意してください。最初の要素のみを作成する必要があり、C ++ 0xでのみ許可されていることに注意してください。現在の標準では、自明な構成可能な型に固執する必要があります。そして、デストラクタはありません。
Potatoswatter

72

C ++は標準です。隠し機能はありません...

C ++はマルチパラダイム言語であり、隠された機能があることに最後のお金を賭けることができます。多くの1つの例:テンプレートメタプログラミング。標準委員会の誰も、コンパイル時に実行されるチューリング完全なサブ言語が存在することを意図していませんでした。


65

Cで機能しないもう1つの隠された機能は、単項の機能です。 +子の。あなたはそれを使ってあらゆる種類のものを促進し腐敗させることができます

列挙型を整数に変換する

+AnEnumeratorValue

また、以前は列挙型であった列挙子の値は、その値に適合することができる完全な整数型になりました。手動では、そのタイプはほとんどわかりません。これは、たとえば、列挙にオーバーロードされた演算子を実装する場合に必要です。

変数から値を取得する

クラス外の定義なしでクラス内の静的初期化子を使用するクラスを使用する必要がありますが、リンクに失敗することがありますか?オペレーターは、そのタイプにassumptinまたは依存関係を作成せずに一時ファイルを作成するのに役立ちます

struct Foo {
  static int const value = 42;
};

// This does something interesting...
template<typename T>
void f(T const&);

int main() {
  // fails to link - tries to get the address of "Foo::value"!
  f(Foo::value);

  // works - pass a temporary value
  f(+Foo::value);
}

配列をポインタに減衰させる

関数に2つのポインタを渡したいのですが、機能しませんか?オペレーターがお手伝いします

// This does something interesting...
template<typename T>
void f(T const& a, T const& b);

int main() {
  int a[2];
  int b[3];
  f(a, b); // won't work! different values for "T"!
  f(+a, +b); // works! T is "int*" both time
}

61

const参照にバインドされた一時的なデータの寿命は、ほとんど知られていないものです。あるいは、少なくとも、ほとんどの人が知らない私のお気に入りのC ++知識です。

const MyClass& x = MyClass(); // temporary exists as long as x is in scope

3
詳しく説明できますか?あなたがただからかっているように;)
ジョセフ・ガービン

8
ScopeGuard(ddj.com/cpp/184403758)は、この機能を活用する良い例です。
MSN、

2
私はジョセフ・ガービンと一緒です。啓発してください。
Peter Mortensen、

私はコメントでやっただけです。また、const参照パラメーターを使用することは当然の結果です。
MSN


52

頻繁に使用されない優れた機能は、関数全体のtry-catchブロックです。

int Function()
try
{
   // do something here
   return 42;
}
catch(...)
{
   return -1;
}

主な使用法は、例外を他の例外クラスに変換して再スローするか、例外と戻り値ベースのエラーコード処理の間で変換することです。


returnFunction Tryのcatchブロックからはできません。再スローのみです。
コンスタンティン

上記をコンパイルしてみましたが、警告は出されませんでした。上記の例はうまくいくと思います。
vividos 2008年

7
returnはコンストラクタに対してのみ禁止されています。コンストラクターの関数tryブロックは、ベースとメンバーを初期化するエラーをキャッチします(関数tryブロックが関数内で単にtryを実行する以外の何かを行う唯一の場合)。再スローしないと、オブジェクトが不完全になります。
puetzk 2008

はい。これは非常に便利です。例外をキャッチしてHRESULTSを返すマクロBEGIN_COM_METHODおよびEND_COM_METHODを記述して、COMインターフェイスを実装するクラスから例外がリークしないようにしました。うまくいきました。
スコットランガム、

3
@puetzkで指摘されているように、これは、基本クラスのコンストラクターやデータメンバーのコンストラクターなど、コンストラクターの初期化リスト内の何かによってスローされた例外を処理する唯一の方法です。
anton.burger 2010年

44

identity/ idメタ関数については多くの人が知っていますが、テンプレート以外の場合に便利なユースケースがあります。

// void (*f)(); // same
id<void()>::type *f;

// void (*f(void(*p)()))(int); // same
id<void(int)>::type *f(id<void()>::type *p);

// int (*p)[2] = new int[10][2]; // same
id<int[2]>::type *p = new int[10][2];

// void (C::*p)(int) = 0; // same
id<void(int)>::type C::*p = 0;

C ++宣言の解読に大いに役立ちます!

// boost::identity is pretty much the same
template<typename T> 
struct id { typedef T type; };

興味深いですが、最初は実際にそれらの定義のいくつかを読むのがもっと困難でした。C ++宣言の裏返しの問題を修正する別の方法は、いくつかのテンプレートタイプエイリアスを作成することtemplate<typename Ret,typename... Args> using function = Ret (Args...); template<typename T> using pointer = *T;pointer<function<void,int>> f(pointer<function<void,void>>);pointer<void(int)> f(pointer<void()>);function<pointer<function<void,int>>,pointer<function<void,void>>> f;
です。-

42

非常に隠された機能は、if条件内で変数を定義でき、そのスコープはifおよびそのelseブロックにのみ及ぶことです。

if(int * p = getPointer()) {
    // do something
}

一部のマクロは、たとえば次のような「ロックされた」スコープを提供するためにそれを使用します。

struct MutexLocker { 
    MutexLocker(Mutex&);
    ~MutexLocker(); 
    operator bool() const { return false; } 
private:
    Mutex &m;
};

#define locked(mutex) if(MutexLocker const& lock = MutexLocker(mutex)) {} else 

void someCriticalPath() {
    locked(myLocker) { /* ... */ }
}

また、BOOST_FOREACHは内部で使用します。これを完了するには、ifだけでなくスイッチでも可能です。

switch(int value = getIt()) {
    // ...
}

そしてwhileループで:

while(SomeThing t = getSomeThing()) {
    // ...
}

(およびfor条件でも)。しかし、私はこれらがそれほど有用であるかどうかあまりわかりません:)


きちんと!私はあなたがそれができるとは思っていませんでした...エラーの戻り値でコードを書くとき、それはいくつかの手間を節約します(そしてするでしょう)。この形式で単に!= 0の代わりに条件を保持する方法はありますか?if((int r = func())<0)が機能しないようです...
puetzk 2008

プエツクはありません。しかしあなたがそれを気に入ってうれしい:)
ヨハネス・シャウブ-litb 09年

4
@Frerich、これはCコードではまったく不可能です。あなたはを考えていると思いますがif((a = f()) == b) ...、この答えは実際には条件内の変数を宣言しています。
ヨハネスシャウブ-litb 2009年

1
@Angryは、変数の宣言のブール値がすぐにテストされるため、非常に異なります。forループへのマッピングもあります。これは、for(...; int i = foo(); ) ...;これiがtrueである限りボディを通過し、毎回初期化されるようです。表示するループは、変数宣言を示しているだけで、同時に条件として機能する変数宣言は示していません:)
Johannes Schaub-litb

5
この機能の使用目的が動的ポインタキャストであるとは言わなかった以外は、非常に良いと思います。
mmocny

29

カンマ演算子が演算子のオーバーロードを呼び出すのを防ぐ

場合によってはカンマ演算子を有効に使用しますが、ユーザー定義のカンマ演算子が邪魔にならないようにしたい場合があります。たとえば、左側と右側の間のシーケンスポイントに依存したり、必要なものに干渉しないようにしたい場合などです。アクション。これがvoid()ゲームの登場です。

for(T i, j; can_continue(i, j); ++i, void(), ++j)
  do_code(i, j);

条件とコードに入力したプレースホルダーを無視します。重要なのはvoid()、組み込みのコンマ演算子を使用するようコンパイラーに強制するです。これは、トレイトクラスを実装する場合にも役立ちます。


これを使って、やりすぎ式の無視を終了しました。:)
GManNickG 2011

28

コンストラクターでの配列の初期化。たとえば、クラスで私たちは、の配列がある場合intなどに:

class clName
{
  clName();
  int a[10];
};

次のように、コンストラクターで配列のすべての要素をデフォルトに初期化できます(配列のすべての要素をゼロに初期化できます)。

clName::clName() : a()
{
}

6
これは、任意の場所の任意の配列で行うことができます。
Potatoswatter 2010

@Potatoswatter:最も厄介な解析のため、見た目よりも難しい。おそらく戻り値を除いて、他にできることは考えられません
Mooing Duck

配列の型がクラス型の場合、これは必要ありませんよね?
Thomas Eding

27

ああ、代わりにペット嫌いのリストを思いつくことができる:

  • 多態的に使用する場合、デストラクタは仮想である必要があります
  • メンバーはデフォルトで初期化される場合とそうでない場合があります
  • ローカルのクラスをテンプレートパラメータとして使用することはできません(それらの有用性が低くなります)
  • 例外指定子:便利に見えるが、そうではない
  • 関数オーバーロードは、異なるシグネチャを持つ基本クラス関数を非表示にします。
  • 国際化に関する有用な標準化はありません(移植可能な標準ワイド文字セット、誰か?C ++ 0xまで待つ必要があります)

プラス側

  • 隠された機能:関数はブロックを試みます。残念ながら私はそれの用途を見つけていません。はい、私は彼らがそれを追加した理由を知っていますが、あなたはそれを無意味にするコンストラクタに再スローする必要があります。
  • コンテナを変更した後のイテレータの有効性に関するSTLの保証を注意深く検討することは価値があります。これにより、少しだけ優れたループを作成できます。
  • ブースト-それはほとんど秘密ではありませんが、使用する価値があります。
  • 戻り値の最適化(明確ではありませんが、標準で明確に許可されています)
  • ファンクタは関数オブジェクト、つまりoperator()としても知られています。これはSTLで広く使用されています。実際には秘密ではありませんが、演算子のオーバーロードとテンプレートの気の利いた副作用です。

16
ペット嫌い:すべての言語がC関数の呼び出しを保証できるので誰もが使用するCアプリとは異なり、C ++アプリにはABIが定義されていません。
gbjbaanb 2008

8
デストラクタは、ポリモーフィックに破壊する場合にのみ仮想である必要があります。これは、最初のポイントと少し微妙に異なります。
デビッドロドリゲス-ドリベス2009年

2
C ++ 0xでは、ローカルタイプをテンプレートパラメータとして使用できます。
tstenner 2009

1
C ++ 0xでは、オブジェクトに仮想関数(つまり、vtable)がある場合、デストラクタは仮想になります。
マッケ

NRVOを忘れないでください。もちろん、プログラムの出力を変更しない限り、どのような最適化も許可されます
jk。

26

未定義の動作なしで、予期されるセマンティクスで、任意のクラスの保護されたデータおよび関数メンバーにアクセスできます。方法を確認するために読んでください。欠陥レポートも読むこれに関する。

通常、C ++は、そのクラスが基本クラスであっても、クラスのオブジェクトの静的でない保護されたメンバーにアクセスすることを禁止します

struct A {
protected:
    int a;
};

struct B : A {
    // error: can't access protected member
    static int get(A &x) { return x.a; }
};

struct C : A { };

それは禁止されています。あなたとコンパイラは、参照が実際に何を指しているのかわかりません。それはCオブジェクトである可能性があり、その場合、クラスにBはそのビジネスについてのビジネスと手がかりがありません。そのようなアクセスはx、が派生クラスまたはその派生クラスへの参照である場合にのみ許可されます。そして、次の例のように、メンバーを読み取る「スローアウェイ」クラスを作成するだけで、任意のコードが保護されたメンバーを読み取ることを許可できますstd::stack

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        static std::deque<int> &get(std::stack<int> &s) {
            // error: stack<int>::c is protected
            return s.c;
        }
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = pillager::get(s);
}

確かに、あなたが見るように、これはあまりにも多くの損傷を引き起こすでしょう。しかし今、メンバーポインターはこの保護を回避することを可能にします!重要な点は、メンバーポインターの型が、アドレスを取得するときに指定したクラスではなく、実際にそのメンバーを含むクラスにバインドされていることです。これにより、チェックを回避できます

struct A {
protected:
    int a;
};

struct B : A {
    // valid: *can* access protected member
    static int get(A &x) { return x.*(&B::a); }
};

struct C : A { };

そしてもちろん、このstd::stack例でも動作します。

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        static std::deque<int> &get(std::stack<int> &s) {
            return s.*(pillager::c);
        }
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = pillager::get(s);
}

メンバー名をパブリックにし、基本クラスのメンバーを参照する、派生クラスのusing宣言を使用すると、さらに簡単になります。

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        using std::stack<int>::c;
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = s.*(&pillager::c);
}


26

もう1つの隠された機能は、関数ポインターまたは参照に変換できるクラスオブジェクトを呼び出すことができることです。それらの結果に対してオーバーロードの解決が行われ、引数は完全に転送されます。

template<typename Func1, typename Func2>
class callable {
  Func1 *m_f1;
  Func2 *m_f2;

public:
  callable(Func1 *f1, Func2 *f2):m_f1(f1), m_f2(f2) { }
  operator Func1*() { return m_f1; }
  operator Func2*() { return m_f2; }
};

void foo(int i) { std::cout << "foo: " << i << std::endl; }
void bar(long il) { std::cout << "bar: " << il << std::endl; }

int main() {
  callable<void(int), void(long)> c(foo, bar);
  c(42); // calls foo
  c(42L); // calls bar
}

これらは「代理呼び出し機能」と呼ばれます。


1
それらの結果に対してオーバーロードの解決が行われたと言うとき、それは実際にそれを両方のFunctorに変換してからオーバーロードの解決を行うということですか?演算子Func1 *()と演算子Func2 *()で何かを印刷してみましたが、どの変換演算子を呼び出すかがわかると、正しいものを選択するようです。
ナビゲーター

3
@navigator、うんそれは概念的に両方に変換してから、最高のものを選びます。実際にそれらを呼び出す必要はありません。これは、結果タイプから、それらがすでに何を生成するかを認識しているためです。最終的に選択されたものが判明したときに、実際の呼び出しが行われます。
ヨハネスシャウブ-litb 2010

26

非表示の機能:

  1. 純粋仮想関数は実装を持つことができます。一般的な例は、純粋な仮想デストラクタです。
  2. 関数がその例外仕様にリストされていない例外をスローしたが、関数が std::bad_exception、そのその例外仕様にある場合、例外はに変換さstd::bad_exceptionれて自動的にスローされます。そうすれば、少なくともbad_exceptionがスローされたことを知ることができます。詳細はこちら

  3. 関数tryブロック

  4. クラステンプレート内のtypedefを明確にするためのテンプレートキーワード。メンバーテンプレート専門分野の名前がの後にある.場合、->または::オペレータ、およびその名前は明示的に修飾テンプレートパラメータ、キーワードテンプレートとプレフィックスメンバーテンプレート名を持っています。詳細はこちら

  5. 関数パラメーターのデフォルトは実行時に変更できます。詳細はこちら

  6. A[i] と同じように機能します i[A]

  7. クラスの一時インスタンスを変更できます!非constメンバー関数は、一時オブジェクトで呼び出すことができます。例えば:

    struct Bar {
      void modify() {}
    }
    int main (void) {
      Bar().modify();   /* non-const function invoked on a temporary. */
    }

    詳細はこちら

  8. :三項(?:)演算子式の前後に2つの異なる型が存在する場合、結果の式の型は、2つの演算子の中で最も一般的な型になります。例えば:

    void foo (int) {}
    void foo (double) {}
    struct X {
      X (double d = 0.0) {}
    };
    void foo (X) {} 
    
    int main(void) {
      int i = 1;
      foo(i ? 0 : 0.0); // calls foo(double)
      X x;
      foo(i ? 0.0 : x);  // calls foo(X)
    }

Pダディ:A [i] == *(A + i)== *(i + A)== i [A]
abelenky 2009年

コミュテーションを取得します。これは、[]自体に意味値がないことを意味し、「x [y]」が「(*((x)+(y ))) "。私が期待していたことはまったくありません。なぜこのように定義されているのでしょうか。
Pダディ

Cとの下位互換性
jmucchiello 2009年

2
最初のポイントについて:純粋仮想関数実装しなければならない特別なケースが1つあります。それは、純粋仮想デストラクタです。
Frerich Raabe、

24

map::operator[]キーがない場合はエントリを作成し、デフォルトで作成されたエントリ値への参照を返します。だからあなたは書くことができます:

map<int, string> m;
string& s = m[42]; // no need for map::find()
if (s.empty()) { // assuming we never store empty values in m
  s.assign(...);
}
cout << s;

これを知らないC ++プログラマーの数に驚かされます。


11
そして、あなたはconstの地図上]、[演算子を使用することはできません反対側の端に
デビッド・ロドリゲス- dribeas

2
ニックの+1 .find()。知らない人は気が狂うことがあります。
LiraNuna

または「const map::operator[]エラーメッセージを生成する」
誰か誰かが

2
言語の機能ではなく、標準テンプレートライブラリの機能です。operator []は有効な参照を返すため、これもかなり明白です。
Ramon Zarazua B.10年

2
これが機能であることを理解するために、マップがしばらく動作しないC#でマップを使用する必要がありました。使うよりもいらいらするのではないかと思ったのですが、間違っていたようです。C#にはありません。
sbi 2010

20

名前のない名前空間に関数または変数を配置すると、を使用staticしてファイルスコープに制限することが非推奨になります。


「deprecates」は強力な用語です...
Potatoswatter

@Potato:古いコメントですが、標準では名前空間スコープでのstaticの使用は非推奨であり、名前のない名前空間が優先されます。
GManNickG 2010

@GMan:問題ありません、私はSOページが本当に「死ぬ」とは思いません。話の両側だけのためにstatic、グローバルスコープでは決して非推奨ではありません。(参考:C ++ 03§D.2)
Potatoswatter 2010

ああ、よく読んで、「グローバル名前空間で宣言された名前には、グローバル名前空間スコープ(グローバルスコープとも呼ばれる)があります。」それは本当にそれを意味するのでしょうか?
Potatoswatter 2010

@ポテト:うん。:) staticuseは、クラス型または関数内でのみ使用する必要があります。
GManNickG 2010

19

クラステンプレートで通常のフレンド関数を定義するには、特別な注意が必要です。

template <typename T> 
class Creator { 
    friend void appear() {  // a new function ::appear(), but it doesn't 
                           // exist until Creator is instantiated 
    } 
};
Creator<void> miracle;  // ::appear() is created at this point 
Creator<double> oops;   // ERROR: ::appear() is created a second time! 

この例では、2つの異なるインスタンス化によって2つの同一の定義が作成されます(ODRの直接違反)

したがって、クラステンプレートのテンプレートパラメーターが、そのテンプレートで定義されているすべてのフレンド関数のタイプに表示されることを確認する必要があります(特定のファイルでクラステンプレートの複数のインスタンス化を防止したい場合を除きます)。これを前の例のバリエーションに適用してみましょう。

template <typename T> 
class Creator { 
    friend void feed(Creator<T>*){  // every T generates a different 
                                   // function ::feed() 
    } 
}; 

Creator<void> one;     // generates ::feed(Creator<void>*) 
Creator<double> two;   // generates ::feed(Creator<double>*) 

免責事項:このセクションを貼り付けました C ++テンプレート:完全ガイド /セクション8.4


18

void関数はvoid値を返すことができます

あまり知られていないが、次のコードは問題ない

void f() { }
void g() { return f(); }

同様に、次の奇妙に見えるもの

void f() { return (void)"i'm discarded"; }

このことを知っていれば、いくつかの分野で活用できます。1つの例:void関数は値を返すことはできませんが、non-voidでインスタンス化される可能性があるため、何も返さない場合もあります。のエラーを引き起こすローカル変数に値を格納する代わりにvoid、値を直接返すだけです

template<typename T>
struct sample {
  // assume f<T> may return void
  T dosomething() { return f<T>(); }

  // better than T t = f<T>(); /* ... */ return t; !
};

17

ファイルを文字列のベクトルに読み込みます。

 vector<string> V;
 copy(istream_iterator<string>(cin), istream_iterator<string>(),
     back_inserter(V));

istream_iterator


8
または:vector <string> V((istream_iterator <string>(cin))、istream_iterator <string>);
UncleBens

5
あなたが意味するvector<string> V((istream_iterator<string>(cin)), istream_iterator<string>());-第二のparam後に行方不明の括弧
knittl

1
これは実際には非表示のC ++機能ではありません。STL機能の詳細。STL!=言語
Nick Bedford

14

ビットフィールドをテンプレート化できます。

template <size_t X, size_t Y>
struct bitfield
{
    char left  : X;
    char right : Y;
};

私はまだこれの目的を考え出していませんが、確かに私を驚かせたことは確かです。


1
私は最近のnビット演算のためにそれを提案したところ、こちらを参照してください:stackoverflow.com/questions/8309538/...
sehe

14

プログラミング言語の中で最も興味深い文法の1つ。

これらのうち3つは一緒に属し、2つはまったく異なるものです...

SomeType t = u;
SomeType t(u);
SomeType t();
SomeType t;
SomeType t(SomeType(u));

3番目と5番目を除くすべてがSomeTypeスタック上のオブジェクトを定義し、それを初期化します(u最初の2つの場合と4番目のデフォルトコンストラクターを使用)。3番目はパラメーターをとらずにを返す関数を宣言していますSomeType。5番目は同様に宣言しています型の値によって一つのパラメータを取る関数SomeTypeの名前u


1番目と2番目の間に違いはありますか?ただし、どちらも初期化です。
Özgür

Comptrol:そうは思いません。どちらもコピーコンストラクタを呼び出すことになりますが、最初の1つは代入演算子のように見えますが、実際にはコピーコンストラクタです。
abelenky 2009年

1
uがSomeTypeとは異なるタイプの場合、最初のものは最初に変換コンストラクタを呼び出し、次にコピーコンストラクタを呼び出しますが、2番目のものは変換コンストラクタのみを呼び出します。
Eclipse、

3
1番目はコンストラクタの暗黙的な呼び出し、2番目は明示的な呼び出しです。次のコードで違いを確認してください。#include <iostream> class sss {public:explicit sss(int){std :: cout << "int" << std :: endl; }; sss(double){std :: cout << "double" << std :: endl; }; }; int main(){sss ddd(7); // intコンストラクタを呼び出しますsss xxx = 7; // doubleコンストラクタを呼び出して0を返す; }
キリルV.リヤドビンスキー2009年

True-コンストラクタが明示的に宣言されている場合、最初の行は機能しません。
Eclipse

12

前方宣言を取り除く:

struct global
{
     void main()
     {
           a = 1;
           b();
     }
     int a;
     void b(){}
}
singleton;

?:演算子を使用してスイッチステートメントを作成する:

string result = 
    a==0 ? "zero" :
    a==1 ? "one" :
    a==2 ? "two" :
    0;

すべてを1行で行う:

void a();
int b();
float c = (a(),b(),1.0f);

memsetなしの構造体のゼロ化:

FStruct s = {0};

角度と時間の値の正規化/折り返し:

int angle = (short)((+180+30)*65536/360) * 360/65536; //==-150

参照の割り当て:

struct ref
{
   int& r;
   ref(int& r):r(r){}
};
int b;
ref a(b);
int c;
*(int**)&a = &c;

2
FStruct s = {};さらに短いです。
コンスタンティン

最後の例では、次のようにするとより簡単になります。b(); float c = 1.0f;
Zifre 2009

2
この構文は「float c =(a()、b()、1.0f);」です。assigment-operation( "c"の代入)を強調するのに役立ちます。代入操作は、非推奨のIMOになる可能性が低いため、プログラミングにおいて重要です。理由がわからない。プログラム状態がフレームごとに再割り当てされる関数型プログラミングに関係している可能性がある。PS。いいえ、「int d =(11,22,1.0f)」は「1」に等しくなります。VS2008で1分前にテストされました。
AareP 2009年

2
+1 電話しては いけませんmainか?私がお勧めしたいglobal().main();とちょうど(シングルトンを忘れて、あなたはそれの寿命が延長され得る、一時的とだけ仕事をすることができます
sehe

1
参照の割り当てが移植性があるとは思えません。しかし、私はこの宣言を放棄する構造が大好きです。
トーマス・エディング

12

三項条件演算子で?:は、その2番目と3番目のオペランドに「適切な」タイプ(非公式に話す)が必要です。ただし、この要件には1つの例外があります(しゃれが意図されています)。2番目または3番目のオペランドvoidは、他のオペランドの型に関係なく、(typeを持つ)スロー式にすることができます。

つまり、?:演算子を使用して、次の正しく有効なC ++式を記述できます。

i = a > b ? a : throw something();

ところで、throw式が実際に(型のvoid)式であり、ステートメントではないという事実は、C ++言語のもう1つのあまり知られていない機能です。これは、とりわけ、次のコードが完全に有効であることを意味します

void foo()
{
  return throw something();
}

この方法で行うことにはあまり意味がありません(おそらく、いくつかの一般的なテンプレートコードでは、これが便利になる場合があります)。


それの価値について、ニールはこれについて質問があります:stackoverflow.com/questions/1212978/…、追加情報のみ。
GManNickG

12

支配ルールは便利ですが、ほとんど知られていません。これは、基本クラスラティスを通る一意でないパス内であっても、メンバーが仮想基本クラスに属している場合、部分的に非表示のメンバーの名前検索は一意であると述べています。

struct A { void f() { } };

struct B : virtual A { void f() { cout << "B!"; } };
struct C : virtual A { };

// name-lookup sees B::f and A::f, but B::f dominates over A::f !
struct D : B, C { void g() { f(); } };

私はこれを使用して、支配ルールによって最も厳密な整列を自動的に計算する整列サポート実装しました。

これは、仮想関数だけでなく、typedef名、静的/非仮想メンバーなどにも当てはまります。私はそれがメタプログラムで上書き可能な特性を実装するために使用されるのを見てきました。


1
きちんと。struct C例に含めた特別な理由...?乾杯。
Tony Delroy、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.