c++コンパイル期間中に2つのタイプ間で変換可能かどうかを検出
2770 ワード
「modern c++design」を学ぶとき、compile-timeで2つのタイプの間で変換できるかどうかを検出する方法を学びました.
ここでの変換には、int、long、doubleなどのデータ型間の変換も含まれ、ベースクラスとサブクラス間の変換も含まれます(つまり、2つのクラスの前にclass Aがclass Bから継承されているかどうか).しかし、この文章の最も核心的な点はcompile-timeという限定語にある.これらの検出はcompile-timeで行われるためruntimeオーバーヘッドは存在しない.
本の中の解法はsizeofというキーワードを使っている.sizeofといえば、任意の複雑な式に対してsizeofキーワードを使用することができます.最も重要なのは、sizeofの後ろの式がruntimeでは実行されないことです.すなわちsizeof(expr)の値はcompile-timeで決定される.コードの証明は次のとおりです.
上のコードは正常にコンパイルされ、実行後に出力がなく、つまり関数呼び出しがありません.test関数は定義されておらず,この性質は後の実装で用いられることがわかる.sizeofの物語はここまでですが、本題に戻りましょう.
2つのタイプが変換可能かどうかを検出するために主に使用される技術は、関数のリロードと関数マッチング時のタイプとの間の変換である.その利用は主にこのような事実である:関数に対して
f(...) 最下位レベルの変換レベルを表し、その関数マッチングの優先度が最も低く、class Aがclass Baseに変換できる場合、
最初のバージョンを呼び出す必要があります.そうしないと、2番目のバージョンになります.
注意深い友达は、この2つの関数のリロードは、パラメータだけでなく、戻りタイプもリロードされていることに気づいたでしょう.異なる戻りタイプを使用して、前のsizeofキーワードを組み合わせて、私たちのこの文章に完璧な解決策を提供しました.
以下にコードを貼り付けます
上のコードには、最も主要な関数のリロードとsizeofの使用を含む多くのよく知られた顔があります.新しい顔はMakeT()関数です.これは何に使いますか.
実はこの関数は主にTに対するコンストラクション関数がnon-publicの場合と互換性があるためである.MakeT()呼び出しを単純なT()に置き換えると、上記のコードは、Tタイプのコンストラクション関数がnon-publicである場合に効果が得られないが、クライアントが直接呼び出すことができるかどうかは明らかではない.戻りタイプTの関数を導入すると,これを巧みに解決した.
わずか10行のコードでは、compile-timeで2つのタイプの間で変換できるかどうかを検出することができますが、この検出のオーバーヘッドは基本的に0で、関数の呼び出しをする必要はありません.tryはありません.catchは、オブジェクトのコピープロファイルがありません.c++テンプレートプログラミングの妙用は、本当に感心させられます.最後のテストコードは次のとおりです.
出力:
私たちの期待と完全に一致している.このようなテクニックはSTL、boostでも使われています.
自分のレベルが限られていることを考えると、文章の中に間違いがあるのは避けられないので、皆さんの指摘を歓迎します.皆さんも積極的にメッセージを残して、筆者と一緒にプログラミングのことを議論してほしいです.
ここでの変換には、int、long、doubleなどのデータ型間の変換も含まれ、ベースクラスとサブクラス間の変換も含まれます(つまり、2つのクラスの前にclass Aがclass Bから継承されているかどうか).しかし、この文章の最も核心的な点はcompile-timeという限定語にある.これらの検出はcompile-timeで行われるためruntimeオーバーヘッドは存在しない.
本の中の解法はsizeofというキーワードを使っている.sizeofといえば、任意の複雑な式に対してsizeofキーワードを使用することができます.最も重要なのは、sizeofの後ろの式がruntimeでは実行されないことです.すなわちsizeof(expr)の値はcompile-timeで決定される.コードの証明は次のとおりです.
#include
int test();
int test2() {
printf(__func__);
return 1;
}
int main() {
void(sizeof(test()));
void(sizeof(test2()));
}
上のコードは正常にコンパイルされ、実行後に出力がなく、つまり関数呼び出しがありません.test関数は定義されておらず,この性質は後の実装で用いられることがわかる.sizeofの物語はここまでですが、本題に戻りましょう.
2つのタイプが変換可能かどうかを検出するために主に使用される技術は、関数のリロードと関数マッチング時のタイプとの間の変換である.その利用は主にこのような事実である:関数に対して
int f(Base); char f(...);
f(...) 最下位レベルの変換レベルを表し、その関数マッチングの優先度が最も低く、class Aがclass Baseに変換できる場合、
f(A());
最初のバージョンを呼び出す必要があります.そうしないと、2番目のバージョンになります.
注意深い友达は、この2つの関数のリロードは、パラメータだけでなく、戻りタイプもリロードされていることに気づいたでしょう.異なる戻りタイプを使用して、前のsizeofキーワードを組み合わせて、私たちのこの文章に完璧な解決策を提供しました.
以下にコードを貼り付けます
template
class Conversion {
private:
static char Test(U);
static int Test(...);
static T MakeT();
public:
enum { exists = sizeof(Test(MakeT())) == sizeof(char) };
};
上のコードには、最も主要な関数のリロードとsizeofの使用を含む多くのよく知られた顔があります.新しい顔はMakeT()関数です.これは何に使いますか.
実はこの関数は主にTに対するコンストラクション関数がnon-publicの場合と互換性があるためである.MakeT()呼び出しを単純なT()に置き換えると、上記のコードは、Tタイプのコンストラクション関数がnon-publicである場合に効果が得られないが、クライアントが直接呼び出すことができるかどうかは明らかではない.戻りタイプTの関数を導入すると,これを巧みに解決した.
わずか10行のコードでは、compile-timeで2つのタイプの間で変換できるかどうかを検出することができますが、この検出のオーバーヘッドは基本的に0で、関数の呼び出しをする必要はありません.tryはありません.catchは、オブジェクトのコピープロファイルがありません.c++テンプレートプログラミングの妙用は、本当に感心させられます.最後のテストコードは次のとおりです.
#include "conversion.h"
#include
struct B { };
struct D : public B { };
struct T { };
int main() {
using namespace std;
cout << Conversion::exists << endl;
cout << Conversion::exists << endl;
cout << Conversion::exists << endl;
cout << Conversion::exists << endl;
cout << Conversion::exists << endl;
}
出力:
1
1
0
1
0
私たちの期待と完全に一致している.このようなテクニックはSTL、boostでも使われています.
自分のレベルが限られていることを考えると、文章の中に間違いがあるのは避けられないので、皆さんの指摘を歓迎します.皆さんも積極的にメッセージを残して、筆者と一緒にプログラミングのことを議論してほしいです.