2

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?

cigien
  • 57,834
  • 11
  • 73
  • 112
Hydrogen
  • 301
  • 1
  • 8
  • 3
    Please don't just post a link. Try to provide [minimal reproducible examples](https://stackoverflow.com/help/minimal-reproducible-example). – 康桓瑋 Jun 23 '22 at 01:45
  • From my experience, when compilers give different results, usually clang is the right one. Haven't read through your code, could you make it a little shorter as an MCVE? – halfelf Jun 23 '22 at 01:50
  • And when you do post that minimal reproducible example, try to aim at minimal? That link is 130 lines of code, most of which is probably not related to your question. – Barry Jun 23 '22 at 02:18
  • I believe this is short enough as an MCVE: https://godbolt.org/z/e7WeKzxbe. – halfelf Jun 23 '22 at 02:27
  • @halfelf Thanks for your effort, but I reckon ```concept AnyOrder``` should be included too due to MSVC's opinion that this ```concept``` cannot be evaluated during compilation. – Hydrogen Jun 23 '22 at 02:46
  • 2
    [Reduced with no header](https://godbolt.org/z/zhG8zv9bn). – 康桓瑋 Jun 23 '22 at 03:00
  • Pedantic language-lawyer answer: The program has undefined behavior because you are declaring an identifier starting with an underscore in the global namespace, where such names are reserved. I suggest changing the names to something that is allowed. – user17732522 Jun 23 '22 at 03:42
  • 1
    The GCC behavior may just be because GCC doesn't support explicit specialization in a class scope at all since it doesn't implement CWG 727 yet, see https://stackoverflow.com/questions/49707184/explicit-specialization-in-non-namespace-scope-does-not-compile-in-gcc. – user17732522 Jun 23 '22 at 03:47
  • @康桓瑋 Thanks for your reduced example! Can I use your example to submit this bug to Microsoft? – Hydrogen Jun 24 '22 at 03:37
  • @user17732522 Thanks for your note! I am aware that using ```__XYZ``` is illegal in all scopes, but I was building these codes in MSVC initially, therefore I am "naturally" using MSVC C++ extensions... – Hydrogen Jun 24 '22 at 03:39

0 Answers0