17

次の単純化されたコードを想像してください。

#include <iostream>
void foo(const int& x) { do_something_with(x); }

int main() { foo(42); return 0; }

(1) 最適化はさておき、42 が に渡されるとどうなりfooますか?

コンパイラは 42 をどこかに (スタック上の?) 貼り付けて、そのアドレスを に渡しますfooか?

(1a) この状況で何をすべきかを指示する標準に何かありますか (または厳密にはコンパイラ次第ですか)?


ここで、少し異なるコードを想像してください。

#include <iostream>
void foo(const int& x) { do_something_with(x); }

struct bar { static constexpr int baz = 42; };

int main() { foo(bar::baz); return 0; }

私が定義しない限り、リンクしませんint bar::baz;(ODRのため?)。

(2) ODR 以外に、上記の 42 で行ったことをコンパイラーが実行できないのはなぜですか?


単純化する明白な方法は、次のように定義することですfoo

void foo(int x) { do_something_with(x); }

ただし、テンプレートの場合はどうすればよいでしょうか。例えば:

template<typename T>
void foo(T&& x) { do_something_with(std::forward<T>(x)); }

(3)プリミティブ型を値でfoo受け入れるように指示するエレガントな方法はありますか? xそれとも、SFINAEなどで特化する必要がありますか?

編集foo:この質問とは無関係であるため、内部で何が起こるかを変更しました。

4

3 に答える 3

13

コンパイラは 42 をどこかに (スタック上の?) 貼り付けて、そのアドレスを に渡しますfooか?

タイプの一時オブジェクトconst intが作成され、prvalue 式で初期化42され、参照にバインドされます。

実際にfooは、インライン化されていない場合は、スタックにスペースを割り当て、そこに格納42し、アドレスを渡す必要があります。

この状況で何をすべきかを指示する標準に何かありますか(または厳密にはコンパイラ次第ですか)?

[dcl.init.ref] .

ODR 以外に、上記の 42 で行ったことをコンパイラーが実行できないのはなぜですか?

言語によると、参照はオブジェクトにバインドされているため、呼び出しをコンパイルしている時点でbar::bazコンパイラが何をしているのかを正確に認識していない限り、コンパイラはこれが重要であると想定する必要があります。fooたとえば、 がfoo含まれている場合assert(&x == &bar::baz);、 で発火してはなりませんfoo(bar::baz)

(C++17 では、静的データ メンバーbazとして暗黙的にインライン化されconstexprます。個別の定義は必要ありません。)

プリミティブ型を値でfoo受け入れるように指示するエレガントな方法はありますか?x

一般に、参照渡しが実際に問題を引き起こしていることを示すプロファイリング データがない場合、これを行う意味はあまりありませんが、なんらかの理由で本当にそれを行う必要がある場合は、(おそらく SFINAE 制約付きの) オーバーロードを追加すると、行く方法。

于 2017-08-12T01:29:46.227 に答える