Clang error "implicit instantiation of undefined template std::tuple_size<auto>"

Viewed 677

Is there a (clean) work-around to Clang's behavior "undefined template smthg<auto>" ?

Code example :

Additional insight :

  • This issue is not reproductible with GCC nor MSVC/CL,
    thus only affect clang and clang-cl
  • Clang-cl add the following error message :
      error : cannot mangle this 'auto' type yet
    

What I'd expect is that, according the example provided above, to get respectively two types with the same - evaluatable - std::tuple_size_v :

class std::tuple<
   struct std::integral_constant<bool,0>,
   struct std::integral_constant<bool,1>,
   struct std::integral_constant<bool,0>
>
class std::tuple<bool,bool,bool>

Am I missing a point?

Code example :

#include <tuple>
#include <array>
#include <type_traits>

template <template <typename> class trait, typename... Ts>
struct trait_result {

    template <template <typename...> class T>
    using as_t = T<typename trait<Ts>::type...>;
    template <template <typename...> class T>
    constexpr static auto as_v = T{trait<Ts>::value...};
    using as_tuple_t = as_t<std::tuple>;
    constexpr static auto as_tuple_v = std::tuple{trait<Ts>::value...};
};

namespace test
{
    template <typename T>
    using is_int = std::is_same<int, T>;
    using results = trait_result<is_int, char, int, bool>;

    using results_as_tuple = results::as_t<std::tuple>; // ok
    using results_as_tuple_value_type = std::decay_t<decltype(results::as_v<std::tuple>)>; // ko

    static_assert(std::tuple_size_v<results_as_tuple> == std::tuple_size_v<results_as_tuple_value_type>);
}
2 Answers
template <template <typename...> class T>
constexpr static auto as_v = T{trait<Ts>::value...};

this attempts to use the deduction guide of T to generate a type.

If you write

auto as_v = results::as_v<std::tuple>;

the compiler crashes, even before the rest of your work.

Assuming you are willing to do without the deduction guide:

template <template <typename...> class T>
constexpr static T<decltype(trait<Ts>::value)...> as_v = {trait<Ts>::value...};

makes the rest of the code compile.

Now,

template <template <typename...> class T>
constexpr static auto as_v = T<decltype(trait<Ts>::value)...>{trait<Ts>::value...};

also doesn't compile when you type

auto as_v = results::as_v<std::tuple>;

so it might not be the deduction guide that is the problem, but rather constexpr static auto here.

This outside of the class:

template<template<class...>class T, template<class>class trait, class...Ts>
constexpr auto foo = T{trait<Ts>::value...};

together with this inside the class:

template <template <typename...> class T>
constexpr static decltype(foo<T, trait, Ts...>) as_v = foo<T, trait, Ts...>;

gives you deduction guides and no internal compiler errors.

Thanks to @Yakk - Adam Nevraumont, and as conclusion, here is the way I fixed it :

using as_array_t = std::array<std::tuple_element_t<0, decltype(std::tuple{trait<Ts>::value...})>, sizeof...(Ts)>;
constexpr static inline auto as_array_v = as_array_t{trait<Ts>::value...};

This avoid another #if __clang__ /* workaround on clang ICE ... */ in the codebase

[EDIT] For completeness purpose :

  • Even if the code above fixed compilation for Clang, it did not for Clang-CL. The following code still failed to compile, with the same error message.
template <typename T>
using is_int = std::is_same<int, T>;
using results = trait_result<is_int, char, int, bool>;

using results_as_tuple = results::as_t<std::tuple>;
using results_as_tuple_value_type = std::decay_t<decltype(results::as_v<std::tuple>)>; // error here
Related