14

n4640 の 5.2.2/4「関数呼び出し」 (n4659 の8.2.2/4 ) に従って、関数パラメーターは呼び出し元のコンテキストで作成および破棄されます。また、実装では、(実装定義の機能として) 関数パラメーターの破棄を、それを囲む完全な式の最後まで遅らせることができます。選択はunspecifiedではなく、実装定義であることに注意してください。

(これが3.3.3「ブロックスコープ」(n4659の6.3.3)とどのように一致するかは完全には明らかではありません。これは、関数パラメータにブロックスコープがあることを暗示しているようで、3.7.3「自動保存期間」(6.7.3) n4659) では、ブロック スコープ変数のストレージは、それらが作成されたブロックが終了するまで続くと述べています。しかし、私が言葉遣いに何かが欠けている/誤解していると仮定しましょう。明らかに、関数パラメーターには独自のスコープがあります) 。

私の知る限り、ABI では GCC と Clang が関数パラメータの破棄を完全な式の最後まで遅らせる必要があります。つまり、これはこれらのコンパイラの実装定義の動作です。そのような実装では、これらの参照/ポインターが呼び出し式内でのみ使用されている限り、関数パラメーターへの参照/ポインターを返すことは問題ないと思います。

ただし、次の例では GCC でセグメンテーション違反が発生し、Clang では問題なく動作します。

#include <iostream>
#include <string>

std::string &foo(std::string s)
{
  return s;
}

int main()
{
   std::cout << foo("Hello World!") << std::endl;
}

どちらのコンパイラも、ローカル変数への参照を返すことに関する警告を発行しますが、これはここでは完全に適切です。生成されたコードを簡単に調べると、両方のコンパイラが実際にパラメーターの破棄を式の最後まで遅らせていることがわかります。ただし、GCC は引き続き意図的に から「null 参照」を返すfooため、クラッシュが発生します。一方、Clang は「期待どおり」に動作し、その parameter への参照を返しますs。これは、期待される出力を生成するのに十分な時間存続します。

(この場合、GCC は簡単にだますことができます。

std::string &foo(std::string s)
{
  std::string *p = &s;
  return *p;
}

これにより、GCC でのセグメンテーション違反が修正されます。)

この場合、パラメータの「遅い」破壊を保証するという仮定の下で、GCCの動作は正当化されますか? 実装によって寿命が延長されたとしても、関数パラメーターへの参照を返すことは常に未定義であるという標準の他の一節が欠けていますか?

4

3 に答える 3