プロンプトとスコープ
ジェネリックライブラリは、古いツールチェーンでもビルドできるようにしつつ、C++26のパックインデックスを使用して型パックまたは関数引数パックから特定位置の要素を読み取る必要があります。式パックのインデックス指定、型パックのインデックス指定、インデックスの制約、機能検出、および互換性のための実装について説明してください。
これは言語の仕様ルールとエンジニアリングにおけるフォールバックをテストするものです。すべてのコンパイラバージョンがすでにこの機能を実装していると主張してはなりません。
面接官が見ているポイント
- 式パックのインデックス指定と、型パック用のパックインデックス指定子(pack-indexing specifier)を区別できているか。
- インデックスがコンパイル時定数でなければならず、パックのサイズを超えられないことを理解しているか。
- コンパイラ名から推測するのではなく、機能検出に
__cpp_pack_indexingを使用しているか。 - 再帰または
tupleによるフォールバックがAPIの規約を維持しているか。
明確にすべき質問
- 値パック、型パック、またはテンプレートパラメータパックのどれをインデックス指定していますか?
- インデックスはコンパイル時定数ですか、それともランタイム整数ですか?
- どのコンパイラ、言語モード、CIバージョンがサポートされていますか?
- 範囲外のインデックスは制約で失敗すべきですか、それとも呼び出し側でチェックされるべきですか?
- フォールバックは挙動だけでなく、診断情報や計算量も維持する必要がありますか?
30秒での回答
「C++26のパックインデックスは、定数インデックスを用いて式パックの要素を選択します。型パックにはパックインデックス指定子を使用します。インデックスはコンパイル時定数である必要があり、範囲外にすることはできません。ランタイム整数の場合は、依然として配列、タプル、またはディスパッチテーブルが必要です。私なら__cpp_pack_indexingを検出し、利用可能な場合は直接構文を使用し、古いコンパイラでは再帰やタプル操作を使用し、同一の境界およびAPIの挙動をテストします。」
ステップバイステップの設計
1. 2つの形式を分離する
式パックのインデックス指定は関数引数から値を選択し、型パックのインデックス指定はテンプレートパラメータから型を選択します。どちらも位置の選択をテンプレートのインスタンス化処理に移し、手書きの再帰を減らしますが、その構文と以降の使用方法は異なります。
2. コンパイル時評価を必須とする
インデックスは、非型テンプレートパラメータのコンテキストにおける定数式です。ランタイム整数で代用することはできません。ランタイムでの選択が必要な場合は、パックをタプル、配列、または関数テーブルとして具現化し、実行時にディスパッチします。
template <std::size_t I, class... Ts>
using type_at = Ts...[I];
template <std::size_t I, class... Ts>
constexpr decltype(auto) value_at(Ts&&... values) {
return values...[I];
}3. 空のパックおよび範囲外パックの処理
空のパックにはインデックス指定可能な要素がなく、パックサイズ以上のインデックスは無効です。公開テンプレートでは、インデックスをパックサイズ未満に保つrequires制約を用いて早期に拒否すべきです。テストでは、空、先頭、末尾、および範囲外の呼び出しをカバーする必要があります。
4. 機能検出の使用
cppreferenceには、値202311Lを持つ機能テストマクロ__cpp_pack_indexingが記載されています。このマクロを言語モードと合わせて確認し、必要に応じてコンパイラCIを使用してください。コンパイラのバージョン文字列単体では、言語機能の保証規約にはなりません。
5. 古いツールチェーン向けフォールバックの設計
C++26がない場合、型パックには再帰テンプレートやタプルの型選択ユーティリティを使用できます。値パックはまずタプルを形成し、位置アクセスユーティリティを呼び出すことができます。同一の境界制約を維持し、CIで両方のパスをコンパイルします。
6. APIと診断情報の評価
直接のパックインデックス指定は実装を簡潔にしますが、安定したAPIの背後に隠蔽しておくべきです。必要な標準モードを文書化し、診断情報にインデックスとパックサイズが表示されるようにし、ソースコードの行数だけを比較するのではなく、両方のパスのコンパイル時間と生成コードをベンチマーク評価します。
質の高い模範解答
「まず、インデックス指定する対象を特定します。C++26の式パックのインデックス指定は値を選択し、型パックにはパックインデックス指定子を使用します。インデックスはコンパイル時定数であるため、空のパックや範囲外のパックは制約の境界で失敗させる必要があります。__cpp_pack_indexingを検出し、対応しているツールチェーンでは直接構文を使用し、古いツールチェーンでは同じAPIの背後で再帰、tuple_element、およびgetを使用します。ランタイムインデックスにはタプルまたはディスパッチテーブルが必要です。テストでは、空、境界、範囲外、両方の言語モード、および両方の実装をカバーします。」
よくある間違い
- テンプレートインデックスとしてランタイム整数を使用する → 言語仕様として定数が必要 → タプル、配列、またはディスパッチテーブルを使用する。
__cplusplusのみをチェックする → 機能サポートは実装状況に依存する →__cpp_pack_indexingおよびCIをチェックする。- 境界を暗黙のままにしておく → インスタンス化の深い場所でエラーが発生する → 早い段階でインターフェースを制約する。
- 型パックのみを実装する → 値パックの呼び出しには依然として再帰が必要 → 両方の形式を個別にテストする。
- フォールバックでセマンティクスを変更する → ツールチェーンごとに挙動が異なる原因になる → 制約、テスト、および安定したAPIを共有する。
フォローアップの質問と回答
パックインデックスでランタイムインデックスを解決できますか?
いいえ。コンパイル時定数が必要です。ランタイムの選択には、タプル、配列、関数テーブル、またはその他のランタイムディスパッチ構造が必要です。
なぜrequires制約を追加するのですか?
言語自体が最終的に範囲外のインデックスを拒否しますが、明示的な制約を設けることで、より早期かつ明確なインターフェース診断情報が得られ、フォールバックでも同じ規約を公開できるようになります。
再帰フォールバックは常に遅いですか?
いいえ。ランタイムコードは同じ結果に最適化される可能性がありますが、テンプレートの深さ、コンパイル時間、および診断情報は異なる場合があります。構文の長さで判断するのではなく、サポートマトリックスとベンチマークで検証してください。