概念评估性&可变模板部分专业化

发布于 2025-02-10 04:30:36 字数 4283 浏览 1 评论 0原文

目前,我遇到了一个问题,三个主要编译器给我带来了三种不同的结果。 (gcc trunk,clang trunk,msvc.v19.latest,均在x86-64下)。

代码&编译器设置在编译器资源管理器上:

#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;
}

https://godbolt.org/z/wzqcccxzeyc 这会导致错误。)

clang认为我的代码是正确的,并且是我预期的;
海湾合作委员会认为我的代码遇到的问题是,我不能专门使用变量模板;
MSVC认为我的代码是错误的,问题在于我的概念>的某些部分无法静态评估。

这里哪个编译器在这里是正确的,我该如何使其用于所有编译器?

Currently, I am running into a problem that three major compilers are giving me three different results. (GCC trunk, Clang trunk, MSVC.v19.latest, all under x86-64).

The code & compiler settings are here on Compiler Explorer:
https://godbolt.org/z/WzqcxzEYc

#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;
}

(I've put a comment next to the lines that cause errors.)

Clang considers my codes to be well-formed and executed as precisely what I intended;
GCC considers the problem my codes have is that I cannot have variable template specialized;
MSVC considers my codes to be ill-formed and the problem is that some part of my concept cannot be statically evaluated.

Which compiler is correct here, and how can I get it to work on all compilers?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文