C++概念可求值性与变量模板偏特化多编译器兼容问题
C++多编译器编译结果不一致问题
在x86-64平台下使用GCC trunk、Clang trunk、MSVC v19.latest三款主流C编译器编译同一段C代码时,三款编译器给出了完全不同的编译结果。触发错误的代码行已在下方完整代码中添加注释标注:
#include <functional> #include <concepts> #include <limits> #include <tuple> #include <array> template <typename... Tys> struct _impl_AnySame; // Undefined. template <> struct _impl_AnySame<> : std::false_type {}; template <typename T> struct _impl_AnySame<T> : std::false_type {}; template <typename T, typename... Tys> struct _impl_AnySame<T, Tys...> { static constexpr bool value = std::disjunction_v<std::is_same<T, Tys>...> || _impl_AnySame<Tys...>::value; }; template <typename T, typename... Tys> concept AnySame = _impl_AnySame<T, Tys...>::value; template<typename... Tys> using tuple_cat_t = std::invoke_result_t<decltype(std::tuple_cat<Tys...>), Tys...>; template<typename T, typename... Tys> using Remove_t = tuple_cat_t<std::conditional_t<std::is_same_v<T, Tys>, std::tuple<>, std::tuple<Tys>>...>; template <typename... Tys> struct _impl_AnyOrder; // Undefined. template <> struct _impl_AnyOrder<std::tuple<>, std::tuple<>> : public std::true_type {}; template <typename... Tys> requires(sizeof...(Tys) > 0) struct _impl_AnyOrder<std::tuple<Tys...>, std::tuple<>> : public std::false_type {}; template <typename... Tys> requires(sizeof...(Tys) > 0) struct _impl_AnyOrder<std::tuple<>, std::tuple<Tys...>> : public std::false_type {}; template <typename T1, typename... Tys1, typename... Tys2> requires(sizeof...(Tys2) > 0) struct _impl_AnyOrder<std::tuple<T1, Tys1...>, std::tuple<Tys2...>> { static constexpr bool value = AnySame<T1, Tys2...> && std::conjunction_v<_impl_AnyOrder<std::tuple<Tys1...>, Remove_t<T1, Tys2...>>>; }; template <typename Tuple_t, typename... Tys> concept AnyOrder = _impl_AnyOrder<Tuple_t, std::tuple<Tys...>>::value; template<typename... Tys> struct VariadicTemplateWrapper { using Tuple_t = std::tuple<Tys...>; static constexpr std::size_t Count_v = sizeof...(Tys); template <typename... Tys2> static constexpr bool Isomer_v = AnyOrder<Tuple_t, Tys2...>; template <typename T> requires(requires{ typename T::Tuple_t; }) static constexpr bool Isomer_v<T> = _impl_AnyOrder<Tuple_t, typename T::Tuple_t>::value; // MSVC generate error from this line: "error C2131: expression did not evaluate to a constant" template <> static constexpr bool Isomer_v<> = Count_v == 0; // GCC generate errors from this line and the line above: "error: explicit template argument list not allowed" }; #ifndef _MSVC_LANG typedef char __int8; typedef short __int16; typedef int __int32; typedef long long __int64; #endif int main(int argc, char** args) noexcept { static_assert(AnySame<char, __int8, __int16, __int32, __int64>); static_assert(!AnySame<float, __int8, __int16, __int32, __int64>); static_assert(AnyOrder<std::tuple<int, float, double>, float, double, int>); static_assert(!AnyOrder<std::tuple<char, float, double>, float, double, bool>); static_assert(!AnyOrder<std::tuple<int, float>, float, double, int>); static_assert(!AnyOrder<std::tuple<int, float, double>, float, double>); using CharacterSet = VariadicTemplateWrapper<char, char8_t, wchar_t, char16_t, char32_t>; static_assert(CharacterSet::Isomer_v<char16_t, char32_t, char, wchar_t, char8_t>); static_assert(CharacterSet::Isomer_v<VariadicTemplateWrapper<char16_t, char32_t, char, wchar_t, char8_t>>); return EXIT_SUCCESS; }
三款编译器的具体行为
- Clang:判定代码为良构,编译通过后运行结果完全符合代码设计预期
- GCC:编译报错,提示类内不允许编写带显式模板参数列表的变量模板特化,报错起始位置为上述代码中两个
Isomer_v的定义行 - MSVC:编译报错,提示代码中部分concept表达式无法求值为常量,判定代码为非法格式,报错起始位置为带requires约束的
Isomer_v重载行
待解答问题
- 上述三款编译器的行为中,哪一款符合C++标准规范要求?
- 如何修改代码,可实现GCC、Clang、MSVC三大编译器的全兼容?
内容的提问来源于stack exchange,提问作者Hydrogen
相关产品推荐
相关产品推荐

