⛳
autoで宣言した非型テンプレート引数の特殊化における罠(MSVC限定)
環境
clang
だとコンパイルが通りましたので、MSVC
のみの現象と思われます。
概要
C++17
以降では非型テンプレート引数にauto
を使用できるようになっています。
(cpprefjp)
template<auto V> struct test {
static constexpr auto value = N;
};
これをbool
型で特殊化してみましょう。
template<bool B> struct test<B> {
static constexpr auto value = B;
};
別に変なことはありませんよね?
しかし、MSVC
の環境だと、以下のテストはエラーになります。
static_assert(test<true>::value == true); // OK
static_assert(test<33>::value == 33); // ERROR
static_assert(test<size_t(2)>::value == 2); // ERROR
調べてみると、すべてのテストでbool
型での特殊化が適用されています。
試しにsize_t
型の特殊化も追加して書いてみましょう。
template<size_t N> struct test<N> {
static constexpr auto value = N;
};
エラーが出ました。
error C2752: test<true>
: 1 つ以上の部分的特殊化がテンプレート引数リストと一致します。
error C2752: test<33>
: 1 つ以上の部分的特殊化がテンプレート引数リストと一致します。
error C2752: test<2>
: 1 つ以上の部分的特殊化がテンプレート引数リストと一致します。
(´・ω・`)
対応策
どうやらMSVC
では、「auto
宣言した非型テンプレート引数」の「型」での特殊化は役に立たないようです。
対応としては、素直に
template<typename T, T V> struct test {
static constexpr T value = V;
};
template<bool B> struct test<bool, B> {
static constexpr auto value = B;
};
とする方法がありますが、値から型を判別できるのにわざわざ型を入力するのは煩わしいです。
可読性は低下しますが、以下のような書き方もできるでしょう。
template<auto V> struct test {
private:
template<typename T, T W> struct impl {
static constexpr auto value = W;
};
template<bool B> struct imple<bool, B> {
static constexpr auto value = B;
};
public:
static constexpr auto value = imple<decltype(V), V>::value;
};
まとめ
以上、MSVCのバグと思われる挙動についての記事でした。
今回の例のようにbool
型の特殊化をしてしまうと、入力したarithmetic
型なパラメータはすべてbool
型に暗黙的に変換されてしまうので、発見の難しい凶悪なバグだと思います。
Discussion
バグとして報告されているのを発見しました。
2020 年に報告されて現時点で修正されてないとなるとこれから修正されることも期待できないかもしれませんね。
紹介ありがとうございます。検討中のまま忘れ去られた感じに見えますね😓