web-dev-qa-db-ja.com

__func__ポインターの2つのconstexprインスタンスの違いはまだconstexprですか?

これは有効なC++ですか?

_int main() {
    constexpr auto sz = __func__ - __func__;
    return sz;
}
_

GCCとMSVCは問題ないと考えていますが、Clangは問題があると考えています: Compiler Explorer


すべてのコンパイラは、これが問題ないことを認めています: Compiler Explorer

_int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = p;
    constexpr auto sz = p2 - p;
    return sz;
}
_

Clangはこれも好きではありませんが、他のものは問題ありません。 Compiler Explorer

_int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = __func__;
    constexpr auto sz = p2 - p;
    return sz;
}
_

ここは何ですか?無関係なポインタの算術は未定義の動作だと思いますが、___func___は同じポインタを返しますか?わからないので、テストしてみようかなと思いました。私が正しく思い出せば、_std::equal_to_は未定義の動作なしに無関係なポインタを比較できます。

_#include <functional>

int main() {
    constexpr std::equal_to<const char*> eq{};
    static_assert(eq(__func__, __func__));
}
_

Clangは、eq(__func__, __func__)is constexpr であっても、std::equal_to::operator()は定数式ではないと考えています。他のコンパイラは文句を言わない: Compiler Explorer


Clangはこれもコンパイルしません。 ___func__ == __func___が定数式ではないと不平を言う: Compiler Explorer

_int main() {
    static_assert(__func__ == __func__);
}
_
14
Ayxan

C++の__func__は識別子です。特に、特定のオブジェクトを参照します。 From [dcl.fct.def.general]/8

関数ローカルの事前定義変数_­_­func_­_­は、フォームの定義のように定義されます

static const char __func__[] = "function-name";

function-nameは実装定義の文字列です。そのような変数がプログラム内の他のオブジェクトのアドレスとは異なるアドレスを持っているかどうかは指定されていません。

function-local事前定義変数 として、この定義は(まるで)関数ブロックの先頭に表示されます。そのため、そのブロック内で__func__を使用すると、その変数が参照されます。

「その他のオブジェクト」の部分と同様に、変数はオブジェクトを定義します。 __func__は、その変数によって定義されたオブジェクトに名前を付けます。したがって、関数内では、__func__をすべて使用すると同じ変数に名前が付けられます。 variableが他のオブジェクトとは異なるオブジェクトであるかどうかは未定義です。

つまり、fooという名前の関数で、リテラル"foo"を問題の別の場所で使用した場合、実装が変数__func__を持つことは禁止されていませんまた、リテラル"foo"が返すオブジェクトと同じです。つまり、標準では、__func__が出現するすべての関数が文字列リテラル自体とは別にデータを格納する必要はありません。

現在、C++の「あたかも」ルールは、実装がこれから逸脱することを許可していますが、それらは検出できない方法でそれを行うことはできません。したがって、変数自体が他のオブジェクトと異なるアドレスを持っている場合と持っていない場合がありますが、同じ関数での__func__の使用は、同じオブジェクトを参照しているかのように動作する必要があります。

Clangはこのように__func__を実装していないようです。関数名のprvalue文字列リテラルを返すかのように実装しているようです。 2つの異なる文字列リテラルは同じオブジェクトを参照する必要がないため、それらへのポインターを減算するとUBになります。また、定数式のコンテキストでの未定義の動作は不正です。

ここでClangが100%間違っていると言うのをためらっている唯一のことは [temp.arg.nontype]/2 です。

参照型またはポインタ型の非型テンプレートパラメータの場合、定数式の値は参照されません(またはポインタ型の場合、アドレスにならない)。

...

  • 定義済みの_­_­func_­_­変数。

参照してください、これは実装によるいくつかの曖昧さを許容するようです。つまり、__func__は技術的には定数式にすることができますが、テンプレートパラメータでは使用できません。技術的には変数ですが、文字列リテラルのように扱われます。

したがって、あるレベルでは、標準は口の両側から話していると言えます。

13
Nicol Bolas