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


14

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

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

GCCとMSVCは大丈夫だと思っていますが、Clangはそうではないと思っています: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 はconstexpreq(__func__, __func__)であっても定数式ではないと考えています。他のコンパイラは文句を言わない: Compiler Explorerstd::equal_to::operator()


Clangはこれもコンパイルしません。__func__ == __func__定数式ではないと不平を言う:コンパイラエクスプローラ

int main() {
    static_assert(__func__ == __func__);
}

Function_definitionから、__func__as-ifでstatic const char __func__[] = "function-name";あり、同等のものが受け入れられますデモ ...
Jarod42

興味深いことに、それはconstexpr変数を初期化して__func__それをstatic_assertで使用すると機能します...
florestan

@ Jarod42では、これはClangのバグですか?
Ayxan

このような@florestan ?Clangでもコンパイルできません。質問の2番目と3番目の例は、あなたが述べた方法です。1つはコンパイルし、もう1つはコンパイルしません。
Ayxan

1
constexpr評価から完全に削除される可能性があるCWG1962も参照してください__func__
Davis Herring

回答:


13

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

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

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

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

機能-ローカル定義済みの変数、この定義(場合など)は、機能ブロックの先頭に表示されます。そのため、__func__そのブロック内での使用はすべて、その変数を参照します。

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

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

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

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

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

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

...

  • 事前定義された_­_­func_­_変数。

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

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


だから、厳密に言えば__func__、私の質問のすべてのケースで定数式になる可能性がありますよね?したがって、コードはコンパイルされているはずです。
Ayxan

「そのような変数がプログラム内の他のオブジェクトのアドレスと異なるアドレスを持っているかどうかは指定されていません。」についてはどうですか?部?未指定の動作とは、抽象マシンの動作が非決定的であることを意味します。それはconstexprの評価に問題がありますか?__func__アドレスの最初の出現が別のオブジェクトのそれと同じで、2番目の出現が__func__そうでない場合はどうなりますか?確かに、2つのインスタンス間でアドレスが異なるという意味ではありませんが、それでも混乱しています。
Johannes Schaub-litb

@ JohannesSchaub-litb:「「そのような変数がプログラム内の他のオブジェクトのアドレスと異なるアドレスを持っているかどうかは指定されていません。」部分はどうですか?「どうですか?__func__マクロではありません。これは、特定の変数、つまり特定のオブジェクトを指定する識別子です。したがって、__func__同じ関数でを使用すると、同じオブジェクトを参照するglvalueが生成されます。要するに、これが当てはまらないような方法で実装することはできません。
Nicol Bolas

同じオブジェクトを参照する@Nicol。ただし、そのオブジェクトは、ある時点で別のオブジェクトと同じアドレスを持つ場合があります。そして、他の瞬間ではありません。それが問題だと言っているわけではありませんが、この可能性を皆さんに思い出させています。そして、結局のところ、私も間違っている可能性があるので、修正または確認されることを期待してこれを言っています。
Johannes Schaub-litb

@ JohannesSchaub-litb:「しかし、そのオブジェクトが別のオブジェクトと同じアドレスを持っている可能性があります。」C ++オブジェクトモデルでは許可されていません。2つのオブジェクト(どちらか一方がもう一方のオブジェクト内にネストされていない)は、両方を同時に同じストレージのライフタイム内に置くことはできません。そして、問題のオブジェクトは静的な保存期間を持っているので、配置を使用しない限りnew、プログラムが終了するまでどこにも移動しません。
Nicol Bolas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.