I have value_list
struct template:
template <decltype(auto)... values>
struct value_list
{};
and I want to implement a method to be able to access template parameter by index. So I ended up with this solution:
#include <type_traits>
namespace details
{
// Second parameter T is just to get rid of errors like this:
// Ambiguous partial specializations of 'at_impl<0, value_list<1>>'
template <std::size_t I, typename T, typename ValueList>
struct at_impl;
template <decltype(auto) Head, decltype(auto)... Rest>
struct at_impl<0, void, value_list<Head, Rest...>>
{
static constexpr decltype(auto) value = Head;
};
template <std::size_t I, decltype(auto) Head, decltype(auto)... Rest>
struct at_impl<I, int, value_list<Head, Rest...>>
{
static constexpr decltype(auto) value = at_impl<I-1, std::conditional_t<I-1 == 0, void, int>, value_list<Rest...>>::value;
};
} // namespace details
template <std::size_t I, typename ValueList>
struct at
{
static constexpr decltype(auto) value = details::at_impl<I, std::conditional_t<I == 0, void, int>, ValueList>::value;
};
template <std::size_t I, typename ValueList>
inline constexpr decltype(auto) at_v = at<I, ValueList>::value;
which can be used like this:
#include <iostream>
#define LOG(x) \
std::cout << #x << ": " << x << '\n'
int main()
{
static constexpr int i = 5;
const std::size_t index = 0;
LOG((at_v<index, value_list<i>>));
return 0;
}
output: (at_v<index, value_list<i>>): 5
But the problem is that it does not compile if we instantiate value_list
template with reference as argument:
int main()
{
static constexpr int i = 5;
const std::size_t index = 0;
constexpr const int & ri = i;
LOG((at_v<index, value_list<ri>>));
return 0;
}
error: implicit instantiation of undefined template 'details::at_impl<0, void, value_list<i>>'
Why at_impl
does not compile if value_list
contains reference and how to fix this?
Here is link to wanbox if you wish to play with my code.
Well, it seems that at least I figured out how to fix my code to make it work identically under all three major compilers(msvc, gcc and clang). But template at
in this case should be inside value_list
:
#include <tuple>
#include <type_traits>
template <decltype(auto)... Values>
struct value_list
{
template <std::size_t I>
using at_t = std::tuple_element_t<I, std::tuple<decltype(Values)...>>;
template <std::size_t I>
static constexpr at_t<I> at_v = std::get<I>(std::tuple<decltype(Values)...>(Values...));
};
int main()
{
static constexpr int i = 5;
constexpr const int & ri = i;
using list = value_list<'t', ri, i, 10>;
static_assert(std::is_same_v<decltype(list::at_v<0>), const char>);
static_assert(std::is_same_v<decltype(list::at_v<1>), const int &>);
static_assert(std::is_same_v<decltype(list::at_v<2>), const int>);
static_assert(std::is_same_v<decltype(list::at_v<3>), const int>);
static_assert(list::at_v<0> == 't');
static_assert(list::at_v<1> == 5);
static_assert(list::at_v<2> == 5);
static_assert(list::at_v<3> == 10);
static_assert(std::is_same_v<list::at_t<0>, char>);
static_assert(std::is_same_v<list::at_t<1>, const int &>);
static_assert(std::is_same_v<list::at_t<2>, int>);
static_assert(std::is_same_v<list::at_t<3>, int>);
return 0;
}
I have checked that this code successfully compiles by:
Concerning to "why" code provided in my question does not compile I don't have better answer than "unlike gcc and msvc for some reason clang++ cannot compile this code".
As side note:
After reading one of the Sam's answers provided by @TedLyngmo in comment section I still do not get why static_assert(std::is_same_v<list::at_t<2>, int>);
got compiled? Because parameter with index 2 is i
which is unparenthesized id-expression(am I wrong?) then according to the C++17 standard decltype(i)
should be type of the entity named by i
. And as we can clearly see that type is const int
(not int
) because constexpr
implies const
. But in the same time I doubt that all three major compilers can be wrong. Rather I don't take something into account and this is subject for another question.