2017-07-27 8 views
0

私は、任意のものを可能な限り無痛でテキストに変換する一連の関数テンプレートを書いています。例えば、 print(std::pair<int, int> {13, 1});{13, 1}と長いC++で同じメモリレイアウトにする必要があるオブジェクトへのキャストを再解析するのは危険ですか?

std::vector<std::tuple<double, std::string>> vect; 
for(int i=0;i<3;++i) { 
    double root = sqrt(i); 
    vect.push_back({root, "sqrt " + std::to_string(i) }); 
} 
print(vect); 

ウィル出力のようなものを出力します:{ {0, "sqrt 0" }, {1, "sqrt 1"}, {1.41421, "sqrt 2"} }

のは、私は、次の構造体があるとしましょう:

struct point { int x, y; }; 

それはのようなものを書くことはどのように危険です次のコードですか?

std::vector<point> my_points; 
//Add points into my_points; 
print(reinterpret_cast<const std::vector<std::pair<int, int>>&>(my_points)); 

これは、GCCでコンパイルし、私は、誰かがコードを移植しようとした場合、それは失敗する可能性が心配ですが、それは、予想される出力を生成します。

+0

std :: vector とstd :: vector >は異なるクラスなので、reinterpret_castを使用する必要があります。動作は実装に依存する – user5821508

答えて

3

まず、dynamic_castをコードに一度も呼び出さないため、ここでは動的キャスティングを行っていません。あなたがここでやっているのはc-castによって作られたreinterpet_castです。

第2に、いいえ、それは安全ではなく、多くのレベルで未定義の動作です。

1

非常に危険です。あなたは未定義の振る舞いをしているstd::vector<std::pair<int, int>>およびstd::vector<Point>は、互いに関係のない完全に、区切りのクラスである。 1つを別のものに再解釈することは未定義の動作です。

実際、標準で定義されている場合がありますが、これはうまくいかない場合があります。 std::vector<char>std::vector<bool>にキャストしてみてください。 charboolのサイズが同じであっても、両方のベクターは互換性がありません。

あなたがバッファをコピーしないようにしたい場合は、テンプレートを使用することを検討してください:

template<typename T> 
void print(const std::vector<T>& vec) { 
    // ... 
} 

さらに良いことに、ベクトルを強制しないでください。あなたはstd::array<Point, n>を持っている場合、あなたはあなたの機能が動作する場合があります

template<typename T> 
void print(const T& range) { 
    // ... 
} 
+0

私はテンプレートを使用しています。そのため、さまざまなものを印刷することができます。 print関数は、vector/tuple/map/set/pairの要素がprint関数によって印刷される限り、任意のベクトル、タプル、マップ、セット、またはペアを印刷することができます。文字列に変換可能です。私は、印刷機能を書くのではなく、私が上記のことをやることがどれほど危険なのか疑問に思っていました。 –

0

代わりstd::vector<T>を渡すのは、次の2つのconst T*、1 const T*と長さ、または二つのポインタの構造体を渡すことができます。

ここで、特殊化(ケースstd::vector<bool>Why is vector<bool> not a STL container?を参照)が行われていないことがわかりました。また、連続したTのメモリがあることもわかります。今は、あなたが知っていることを提供その

  1. Foo sおよびBarの持っているとまったく同じレイアウト(配置、大きさ)機能がある場合(振る舞いを使用
  2. Foo sおよびBarシェアf(Foo)であり、その関数をprintで使用すると、意味的に同一の関数f(Bar)も存在する必要があります)。より理論的な定式化は、Foofに関してBarに相当します。

その後、それは安全でなければなりませんが、コンパイラは実際に(2)をチェックすることはできませんので、あなたは、自分です。

ある
struct PointXY{float x;float y;}; 

struct PointYX{float y;float x;}; 

float inner_product(PointXY p1,PointXY p2) 
    {return p1.x*p2.x + p1.y*p2.y;} 

float inner_product_with_important_y(PointXY p1,PointXY p2) 
    {return 0.5f*(p1.x*p2.x + 4.0f*p1.y*p2.y);} 

、あなたが安全に、両方が(1)と(2)が満たされているPointXYPointYX(任意の組み合わせでinner_productを呼び出すことができます。例として、ポイントの2つのバージョンを取る(float代わりintを使用することができます) )が、異方性バージョンを呼び出すとすぐに間違った結果が得られます((1)は実行されますが、(2)は実行されません)。

関連する問題