クラスの特殊化におけるclang / gccの不整合


9

特化しようとしているときに、私はこの問題に出くわし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 specialization does not specialize any template argument; to define the primary template, remove the template argument list

struct Test<sfinae_v_t<T, std::is_integral<T>::value>> {};

       ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1 error generated.

Compiler returned: 1

これはどちらかのコンパイラのバグですか、それとも上記のコードはいくつかのUBを呼び出しますか?


3
これはさらに簡略化できます。
1

3
ICCとMSVCはどちらもコンパイルに失敗します。
ChrisMM

@Evgこれをgccコンパイルするのは驚くべきことです。これはコンパイルしないので...
Max Langhof

1
私は(と同じ理由のために完全に間違っていない場合FWIWは、この病気は、形成されるべきである、これは悪い形成されています)。
Max Langhof

1
標準を引用しているので、language-lawyerタグを追加しました。
ギヨームラシコット

回答:


3

以下で(OLD POSTの下で)伝えることはある程度正しいはずですが、これに関する実際の問題は、SFINAEが誤って使用されていることです。したがって、これがgccのバグであるかどうかはわかりません。

エイリアス宣言は常に成功する必要があります。クラスや関数の宣言や特殊化ではないため、エイリアスを特殊化することはできないため、そこでSFINAEを実行することはできません。エイリアス宣言が成功しない場合、プログラムの形式は正しくありません。したがって、コンパイラーは、ユーザーがそのようなテンプレートをインスタンス化するように強制するまで、エイリアス宣言が成功しない場合はないと想定する場合があります。

したがって、プログラムが不正な形式でない場合に発生するため、コンパイラsfinae_v_t<T,...>が常にTであると考えることは完全に許容されます。したがって、プログラムが不正な形式ではないすべてのケースで、部分的な特殊化は特殊化されないため、これが不正な形式であることがわかります。(それはclangが行うことです)。

コンパイラがこれを強制されるとは思わない。そうでなく、「OK、sfinae_v_tなんらかのタイプである」と考えるだけの場合、これが再宣言であることは明らかではありません。したがって、そのうちの1つをインスタンス化するまでは、エラーをスローしないことで問題はないと思います。

しかし、それをインスタンス化するとstd::enable_if、テンプレート引数に応じて、再宣言があるか、プログラムがのために不正な形式であるという問題が発生するはずです。GCCはそれらの少なくとも1つを取得する必要がありますが、どちらも取得しません。

これは、を使用しない簡単な例にもまったく当てはまりませんstd::enable_if。ですから、これはまだGCCのバグだと思いますが、私は十分に心を動かされており、確実にそれを言うことはできません。誰かがそれをバグとして報告し、gccの人々にそれについて考えさせるべきだと私は言うでしょう。

古い投稿

これはgccのバグです。標準は、関数テンプレートでクラステンプレートを変換するためのルールを提供します。部分的な関数テンプレートの順序付けで、その関数が他のテンプレートよりも前にある場合、1つのクラステンプレートは他よりも特殊化されています。

ここで関数を作成しましたが、gcc は関数の呼び出しがあいまいであることを主張しているため、クラステンプレートも同様に指定されていると言う必要があります。

注:標準を注意深く読むと、私の頭の中のコンパイラはclangに同意します。


あるsfinae_v_t<T, std::is_integral_v<T>>sfinae_v_t<T, !std::is_integral_v<T>>同じタイプとして扱わ?意味的にはそうではありません。
1

@GuillaumeRacicotかなり可能性がありますが、その理由を正確に理解したいと思います。たとえば、この規格では、「部分的な特殊化を宣言するときに依存名をチェックすることはできませんが、部分的な特殊化に代入するときにチェックされます」とも記載されています。にsfinae_v_t<T>依存しているので、部分的な特殊化でTを代入してから同じ型かどうかを判断するという意味ではないTですか?その場合、どちらかが不正であるので、それらは同じではありません。
ofo

@ofo言わなければならない、よくわかりません。これらの2つについて考えることは、ちょっとしたことです。そのうちの1つはタイプにはならず、両方を非テンプレートコンテキストで使用すると、が原因でコンパイルエラーが発生するためenable_if_tです。私がこの標準を最もよく読んでいるのは、それらが同じかどうかは問題ではないということです。部分的な順序付けでは、常に1つの関数のtemplareパラメーター形式を他のテンプレート引数形式と比較し(つまり、int既に置換されています)、それらの1つに実際の型があるため、比較する必要はありません。それらを抽象的に。
n314159

1
さらに掘り下げて、これここから見つけまし。SFINAEはテンプレートエイリアスでtemplate<bool B, typename T> enable_if_t = typename enable_if<B, T>::type;問題なく機能するはずです。そうでなければ、機能しません。私は先に進んでgccに対するバグを報告しますが、gccがそこで間違っているかどうかは本当にわかりません。ありがとう。
1
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.