配列サイズの妥当性に基づく専門化


8

配列サイズの妥当性に基づいて特化しようとしています:

// base template
template<int p, typename T = void>
struct absolute {
    operator int () const { return 0; }
};

// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
    operator int () const { return p; }
};

// negative case template
template<int p>
struct absolute<p, typename std::void_t<int[-p]>> {
    operator int () const { return -p; }
};


int main() {
    std::cout << absolute<5>() << std::endl;
    std::cout << absolute<-5>() << std::endl;
    std::cout << absolute<0>() << std::endl;
}

問題#1:

上記のコードはgccでうまく動作しますが、clangでコンパイルできません

Clangがエラーを生成:テンプレート構造体 'absolute'の再定義

誰が正しいのですか?


問題#2:

gccとclangの両方(clangをゲームに戻すために否定的な特殊化を削除する場合)の両方でabsolute<0>()、ベーステンプレートを選択する理由が明確ではありません。何も間違ってありますint[0]してだけでなく、std::void_t<int[0]>より専門ているように見えるそれは:

// base template
template<int p, typename T = void>
struct absolute {
    operator int () const { return -1; }
};

// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
    operator int () const { return p; }
};

int main() {
    std::cout << absolute<5>() << std::endl; // 5
    std::cout << absolute<0>() << std::endl; // -1, why not 0?
}

そして...基本テンプレートが実装なしで宣言されている場合:

// base template
template<int p, typename T = void>
struct absolute;

gccとclangの両方がコンパイル失敗し、呼び出しで不完全な型無効に使用したことを報告しました:absolute<0>()。特殊なケースに当てはまるようですが。

何故ですか?


template<int p> struct absolute<p, typename std::void_t<int[p]>>そしてtemplate<int p> struct absoluteそれは複製ではありませんか?
チップスター

3
int[0]ISO C ++標準timsong-cpp.github.io/cppwp/n4659/dcl.array#1によって禁止されています "その値はゼロより大きくなければなりません"
LF

@LFだからここで失敗しない:godbolt.org/z/sfSxamはclangとgccの両方のバグですか?
アミールカーシュ

@AmirKirsh拡張機能を無効にするのを忘れました。godbolt.org/z/RB96uc
LF

MSVCでもコンパイルします。
eerorika

回答:


4

Clangの再定義エラーについては、この質問を参照してください。

元々、などのエイリアステンプレートのテンプレートIDはstd::void_t、引数の置換エラーをチェックせずに、単にエイリアスタイプで置き換えられていました。これは、CWG問題1558で変更されました。これにより、テンプレートの引数で置換の失敗を要求するように標準が変更されただけですが、エイリアスを置き換えた後に同等となる2つのテンプレートを同等と見なす必要があるかどうかは明確になりません。Clangはそれらを同等と見なしていますが、GCCはそうではありません。これは、オープンCWG問題1980です。


-pedantic-errorsGCCは、のために、すでにハードエラーを報告します

std::cout << absolute<5>() << std::endl;

専門分野で

template<int p>
struct absolute<p, typename std::void_t<int[-p]>>

おそらく配列サイズは定数式ではないからです。配列のサイズは、型の変換された定数式でなければなりませんstd::size_t。変換された定数式は、非ナロー変換のみを使用できます。のは本当である、それはそう-pp = 5、変換のstd::size_t種類作り、定数式ではないint[-p]病気-形成を、私はそれが置換故障ではなく、ハードエラーの原因となるべきだと思います。C ++ 17標準(ドラフトN4659)の[temp.deduct / 8]は次のように述べています。

置換の結果、無効な型または式になる場合、型の推定は失敗します。無効な型または式は、置換された引数を使用して記述された場合、診断が必要で、形式が正しくないものです。

これはここでも当てはまります。引用に続く非規範的な例には、置換の失敗の例として、負の配列サイズも含まれています。

absolute<-5>()GCCが同等のエラーを報告しないのは特に奇妙です

template<int p>
struct absolute<p, typename std::void_t<int[p]>>

特殊化。ここでint[p]評価されint[-5]ますが、変換された定数式のサイズもありません。


absolute<0>()配列サイズはゼロより大きくする必要があるため、プライマリテンプレートを選択し、部分的な特殊化を両方とも実行不可能にします。サイズがゼロの配列は、-pedantic-errorsGCCおよびClangで無効にできる言語拡張です。


独自のバージョンのvoid_t作品をclangとgccの両方に実装します:godbolt.org/z/-2C8mJこれは、CWG問題1558に関連しているようです...またはCWG問題1980も処理しますか?
Amir Kirsh

@AmirKirsh定義を使用すると、CWG問題1980も回避できます。どちらも、非依存型に解決されるエイリアステンプレートの処理方法に関連しています。定義make_void<Ts...>::typeは、依存型であるに解決されます。
クルミ

@AmirKirsh私は、GCCが1つの専門分野のサイズが定数式ではないことについて不平を言うことを知らなかった。私はそれを反映するように回答を更新しました。これにより、GCCに一貫性がないことがさらに明確になったようです。
クルミ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.