タグ付けされた質問 「language-lawyer」

プログラミング言語と環境の正式または信頼できる仕様の複雑さについての質問。

1
クラスの特殊化におけるclang / gccの不整合
特化しようとしているときに、私はこの問題に出くわしtuple_size/ tuple_elementC ++ 17の構造の結合のためのカスタムクラス。 以下のコードはGCCでコンパイルされますが、clangではコンパイルされません(両方のトランクバージョン、以下のリンクを参照)。 #include <type_traits> template<typename T, typename... Ts> using sfinae_t = T; template<typename T, bool... Bs> using sfinae_v_t = sfinae_t<T, typename std::enable_if<Bs>::type...>; template <typename T> struct Test; template <typename T> struct Test<sfinae_v_t<T, std::is_integral_v<T>>> {}; void f() { Test<int> t; } https://godbolt.org/z/ztuRSq これは、clangによって提供されるエラーです。 <source>:13:8: error: class template partial …

2
タイプ・パンニング・テーマのバリエーション:インプレース・トリビアル・コンストラクション
これはかなり一般的なテーマであることは知っていますが、典型的なUBを見つけるのは簡単ですが、今のところこの亜種は見つかりませんでした。 そこで、実際のデータのコピーを避けながら、Pixelオブジェクトを正式に紹介しようとしています。 これは有効ですか? struct Pixel { uint8_t red; uint8_t green; uint8_t blue; uint8_t alpha; }; static_assert(std::is_trivial_v<Pixel>); Pixel* promote(std::byte* data, std::size_t count) { Pixel * const result = reinterpret_cast<Pixel*>(data); while (count-- > 0) { new (data) Pixel{ std::to_integer<uint8_t>(data[0]), std::to_integer<uint8_t>(data[1]), std::to_integer<uint8_t>(data[2]), std::to_integer<uint8_t>(data[3]) }; data += sizeof(Pixel); } return result; // throw in …

1
不完全な型へのポインタは不完全なのでしょうか?
することができint (*)[]、不完全型で? C 2018 6.2.5 1は言う: 翻訳単位内のさまざまな時点で、オブジェクトタイプは不完全(そのタイプのオブジェクトのサイズを決定するのに十分な情報がない)または完全(十分な情報がある)の場合があります。 したがって、型のサイズがわかっていれば、型は完全であるように見えます。6.2.6.1 28は、特定のタイプのポインターは同じサイズ(ポインターvoidと文字、互換タイプへのポインター、構造体へのポインター、および共用体へのポインター)でなければならないことを指定していますが、他のタイプへのポインターは異なる場合があります。 すべてのポインター、またはの配列へのすべてのポインターがint同じサイズであるCの実装では、のサイズint (*)[]がわかっているため、完全なサイズになります。たとえば、大きな配列に異なるポインタを使用する実装では、サイズがわからないため、不完全です。 以下のようにMMが指摘し、構造これは、ポインタのサイズの実装が受け入れなければならないことを示唆している6.7.2.1 3に制約ごとに、最終的な可撓性のアレイメンバーを除き、不完全な型とメンバーを含んではならないstruct { int (*p)[]; }異なる有する実装ながらそのような配列のサイズは、制約違反を診断する必要があります。(これは、そのような宣言が厳密に準拠するCの一部ではないことを意味します。)

1
演算子newとコンストラクターの引数の実行順序
DOES C ++は、順序指定SPEC operator newとのコンストラクタAでをnew C(A())。 g ++は順序をA()-> new-> C()にしますが、clang ++はそれをnew-> A()->にしC()ます。 違いは不特定の行動が原因ですか? g ++:7.4.0 clang ++:10.0.0 #include <iostream> #include <cstdlib> struct A { A() { std::cout << "call A()\n"; } }; struct C { C(A) { std::cout << "call S()\n"; } void *operator new(size_t s) { std::cout << "call …

1
テンプレートと名前検索を理解しようとする
次のコードスニペットを理解しようとしています スニペット#1 template <typename T> struct A { static constexpr int VB = T::VD; }; struct B : A<B> { }; ここでは、gcc9もclang9もエラーをスローしません。 Q.このコードがコンパイルされるのはなぜですか?A<B>Bから継承するときにインスタンス化していませんか?BにはVDがないので、コンパイラーはここでエラーをスローすべきではありませんか? スニペット#2 template <typename T> struct A { static constexpr auto AB = T::AD; // <- No member named AD in B }; struct B : A<B> { …

1
「前に強く起こる」とはどういう意味ですか?
C ++ドラフト標準では、「前に強く起こる」というフレーズが何度か使用されています。 例:終了 [basic.start.term] / 5 std :: atexitへの呼び出し([support.start.term]を参照)の前に、静的ストレージ期間を持つオブジェクトの初期化が強く発生した場合、std :: atexitに渡された関数の呼び出しオブジェクトのデストラクタを呼び出す前にシーケンス化されます。std :: atexitの呼び出しが静的ストレージ期間のオブジェクトの初期化が完了する前に強く発生する場合、オブジェクトのデストラクタの呼び出しは、std :: atexitに渡される関数の呼び出しの前にシーケンスされます。std :: atexitへの呼び出しがstd :: atexitへの別の呼び出しの前に強く発生する場合、2番目のstd :: atexit呼び出しに渡される関数の呼び出しは、関数に渡される前にシーケンスされます最初のstd :: atexit呼び出し。 データレース [intro.races] / 12で定義 評価Aは、評価Dの前に強く発生します。 (12.1)AがDの前にシーケンスされる、または (12.2)AがDと同期し、AとDの両方が順次一貫したアトミック操作([atomics.order])である、または (12.3)評価BとCがあり、AはBの前にシーケンスされ、Bは単にCの前に発生し、CはDの前にシーケンスされる、または (12.4)AがBの前に強く発生し、BがDの前に強く発生するという評価Bがあります。 [注:非公式に、AがBの前に強く発生する場合、AはすべてのコンテキストでBの前に評価されるように見えます。excludeが操作を消費する前に強く発生します。—エンドノート] なぜ「以前に強く起こる」ことが導入されたのですか?直感的に、その違いと「以前の出来事」との関係は何ですか? ノートの「AはすべてのコンテキストでBより先に評価されるように見える」とはどういう意味ですか? (注:この質問の動機は、この回答の下でのPeter Cordesのコメントです。) 追加の標準草案(Peter Cordesに感謝) 順序と一貫性 [atomics.order] / 4 すべてのmemory_order :: seq_cst操作(フェンスを含む)には、次の制約を満たす単一の合計順序Sがあります。まず、AとBがmemory_order :: seq_cst操作で、AがBの前に強く発生する場合、AはSでBの前に発生します。 Bの前に、Sが満たすには、次の4つの条件が必要です。 (4.1)AとBが両方ともmemory_order …

2
std :: dequeは実際には最初に一定時間の挿入を持っていますか?
標準は言う: 両端キューは、ランダムアクセス反復子(27.2.7)をサポートするシーケンスコンテナーです。さらに、最初または最後の一定時間の挿入および消去操作をサポートします。途中で挿入と消去には線形時間がかかります。 ただし、同じ条項にも次のように記載されています。 この条項のすべての複雑さの要件は、含まれているオブジェクトに対する操作の数に関してのみ記述されています。[例:vector<vector<int>>含まれるそれぞれのコピーの複雑さvector<int>自体は線形であるにもかかわらず、タイプのコピーコンストラクターは線形の複雑さを持っています。—最後の例] これは、たとえば両端キューに既に存在するsと挿入される新しいオブジェクトに対して一定数以上の操作を実行しない限り、最初の挿入にdeque<int>線形時間がかかることを意味するのではありませんか?intint たとえば、「サイズKのベクターのベクター」を使用して両端キューを実装するとします。最初にK回挿入するたびに、新しいサイズKのベクトルを最初に追加する必要があるため、他のすべてのサイズKのベクトルを移動する必要があります。これは、最初の挿入の時間の複雑さが償却されたO(N / K)であることを意味します。ここで、Nは要素の総数ですが、Kは定数なので、これは単なるO(N)です。しかし、これは規格で許可されているようです。サイズKのベクトルを移動してもその要素は移動せず、「複雑さの要件」は含まれているintオブジェクトの「操作の数に関してのみ述べられている」ためです。 規格はこれを本当に許可していますか?それとも、より厳しい要件、つまり、含まれているオブジェクトに対する一定数の操作に加えて一定の追加時間があると解釈する必要がありますか?

1
ポインターを使用して読み取り専用フィールドを変更できますか?しかし、なぜ?
私はC ++の出身で、C ++とC#のポインター間で非常に異なる動作を見つけます。 意外にも、このコードはコンパイルできます...そしてさらに...完全に動作します。 class C { private readonly int x = 0; unsafe public void Edit() { fixed (int* p = &x) { *p = x + 1; } Console.WriteLine($"Value: {x}"); } } これは私を非常に困惑させます、C ++でもconstオブジェクトを保護するメカニズムがあります(constC ++ではreadonlyC#ではなくC#とほぼ同じですconst)、ポインターを介してconst値を任意に変更する十分な理由がないためです。 さらに調べてみると、C#にはC ++の低レベルのconstポインターに相当するものはなく、次のようになります。 readonly int* p C#の場合。 その場合、pはポイントされたオブジェクトのみを読み取ることができ、それに書き込むことはできません。 また、constオブジェクトの場合、C#はアドレスを取得する試みを禁止しました。 C ++では、これを変更しようとするconstと、コンパイルエラーまたは未定義の動作になります。C#では、これを利用できる可能性があるかどうかわかりません。 だから私の質問は: この動作は本当に明確ですか?C#では知っていますが、C ++にはUBのような概念はありません …

1
C ++でのif / elseブランチのコンパイル時の削除
次のコードサンプルでは、ifステートメントはboolコンパイル時定数であるテンプレートパラメーターに依存しています。コンパイラはこのコードを別の方法で処理します。 MSVCはリンクエラーで失敗します(これは私が予想したことです)。elseブランチのテンプレート関数にはtrueテンプレートパラメーター値の特殊化がないため(呼び出されることはありません)。 GCCとClangはどちらも問題なくコンパイルされ、実行時の動作は正しいです。これはif、コンパイル時にステートメントを評価し、リンクする前に未使用のブランチを削除するためです。 問題は、どの動作が標準に準拠しているか(または、未定義の動作であり、どちらも独自の方法で正しいか)です。 #include <iostream> template<const bool condition> struct Struct { void print() { if (condition) { std::cout << "True\n"; } else { printIfFalse(); } } private: void printIfFalse(); }; template <> void Struct<false>::printIfFalse() { std::cout << "False\n"; } int main() { Struct<true> withTrue{}; withTrue.print(); Struct<false> withFalse{}; withFalse.print(); return …

1
#1664の提案された解決策を理解する方法
#1664の提案された解決策(提案された解決策1664)を見た後、私は関数テンプレートのデフォルト引数のルールに混乱しています、ここにコンテンツを引用してください: 8.1.5 [expr.prim.lambda]パラグラフ3によると クロージャタイプは、対応するラムダ式を含む最小のブロックスコープ、クラススコープ、または名前空間スコープで宣言されます。[注:これにより、クロージャタイプに関連付けられた名前空間とクラスのセットが決まります(6.4.2 [basic.lookup.argdep])。ラムダ宣言子のパラメータタイプは、これらの関連する名前空間とクラスには影響しません。—エンドノート] ただし、17.8.1 [temp.inst]パラグラフ13には、 デフォルトの引数を使用する必要がある方法で関数テンプレートfが呼び出された場合、依存名が検索され、セマンティクスの制約がチェックされ、デフォルトの引数で使用されているテンプレートのインスタンス化は、デフォルトの引数のように行われます。その時点で使用されている関数テンプレートfと同じスコープ、同じテンプレートパラメータ、同じアクセス権を持つ関数テンプレートの特殊化で使用される初期化子でした。 その場合、可能性は、テンプレート関数(または、おそらく、クラステンプレートのメンバー関数)のデフォルト引数のラムダ式のクロージャタイプが、の本体のブロックスコープで宣言されていると見なされることです。架空の機能テンプレートの特殊化。 次の例を考えてみましょう。 namespace J { inline namespace K { template <typename T> int zap(const T &t) { foo(t); return 0; } template <typename T> void zip(int = zap([] { })) { } } template <typename T> void foo(const T &) { } …

1
参照による `constexpr`メンバー関数の呼び出し-clang vs gcc
次の例を考えてみます(スニペット(0)): struct X { constexpr int get() const { return 0; } }; void foo(const X& x) { constexpr int i = x.get(); } int main() { foo(X{}); } 上記の例は、g++以前のすべてのバージョンでコンパイルされg++ 10.x、でコンパイルされることはありませんclang++。エラーメッセージは次のとおりです。 error: 'x' is not a constant expression 8 | constexpr int i = x.get(); | godbolt.orgの実例 ただしx、の本体で定数式が使用されることはないため、エラーの種類には意味がありますfoo。 X::get()マークされてconstexprおり、の状態には依存しませんx。 に変更const …

2
配列の最初の要素の前のポインタ
Cでは、ポインターが同じ配列またはその配列の終わりを過ぎた1つの要素を参照する場合、演算と比較は明確に定義されていると言われています。次に、配列の最初の要素の前の1つはどうですか?私がそれを逆参照しない限り、それは大丈夫ですか? 与えられた int a[10], *p; p = a; (1)書くことは合法--pですか? (2)p-1式を書くことは合法ですか? (3)(2)が問題ない場合、それを主張できp-1 < aますか? これには実際的な懸念があります。reverse()で終わるC文字列を逆にする関数を考え'\0'ます。 #include <stdio.h> void reverse(char *p) { char *b, t; b = p; while (*p != '\0') p++; if (p == b) /* Do I really need */ return; /* these two lines? */ for (p--; b …

2
Constオーバーロードがgccで予期せず呼び出されました。コンパイラのバグまたは互換性の修正?
charおよびconst char配列のテンプレートのオーバーロードに依存する、はるかに大きなアプリケーションがあります。gcc 7.5、clang、およびビジュアルスタジオでは、以下のコードはすべてのケースで「NON-CONST」を出力します。ただし、gcc 8.1以降の場合、出力は次のようになります。 #include <iostream> class MyClass { public: template <size_t N> MyClass(const char (&value)[N]) { std::cout << "CONST " << value << '\n'; } template <size_t N> MyClass(char (&value)[N]) { std::cout << "NON-CONST " << value << '\n'; } }; MyClass test_1() { char buf[30] = "test_1"; return …

1
memcpyバッファーUBでreinterpret_castを使用していますか?
コードを考える struct A {}; auto obj = new A; std::vector<unsigned char> buffer; buffer.resize(sizeof(obj)); std::memcpy(buffer.data(), &obj, sizeof(obj)); // this copies the pointer, not the object! // ... auto ptr = *reinterpret_cast<A**>(buffer.data()); // is this UB? delete ptr; reinterpret_castこの場合の使用法はUBですか?memcpyはインスタンスの存続期間を開始しないため、厳密なエイリアシングルールに違反しstd::bit_castているため(そういうわけで、C ++ 20に追加されました)。 そして、キャストを別のキャストに置き換えるとmemcpy(ポインターを読み取るため)、プログラムは明確に定義されますか?

2
C ++機能の影響を受けている、またはC ++機能から派生しているC機能はどれですか。[閉まっている]
休業。この質問は意見に基づいています。現在、回答を受け付けていません。 この質問を改善してみませんか?この投稿を編集して、事実と引用で回答できるように質問を更新してください。 5か月前に閉鎖。 元々Cの一部ではなかったが、C ++で/のために発明され、その利点のために後でCに採用された実際のC標準の機能は何ですか? 顕著な例の1つは、1行コメントです//。これは、最初はC ++から来たもので、後にCによって採用されました。 C ++の開発から明示的または暗黙的にもたらされた実際のC標準のその他の機能を知っていますか? 情報:もちろん、C ++はCから派生していることは知っていますが、派生したC ++の開発から採用された機能について考えていました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.