我有
value_list
结构模板:
template <decltype(auto)... values>
struct value_list
{};
我想实现一种能够通过索引访问模板参数的方法。 所以我最终得到了这个解决方案:
#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;
可以这样使用:
#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;
}
输出:
(at_v<index, value_list<i>>): 5
但问题是,如果我们以引用作为参数实例化
value_list
模板,它就无法编译:
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;
}
错误:
implicit instantiation of undefined template 'details::at_impl<0, void, value_list<i>>'
如果
at_impl
包含引用,为什么 value_list
无法编译以及如何解决此问题?
如果您想使用我的代码,这里是 wanbox 的链接。
好吧,看来至少我弄清楚了如何修复我的代码,使其在所有三个主要编译器(msvc、gcc 和 clang)下同样工作。但在这种情况下,模板
at
应该位于 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;
}
我已检查此代码是否已成功编译:
关于我的问题中提供的“为什么”代码无法编译,我没有比“与 gcc 和 msvc 不同,由于某种原因 clang++ 无法编译此代码”更好的答案。
作为旁注: 在阅读了@TedLyngmo在评论部分提供的Sam的答案之一后,我仍然不明白为什么
static_assert(std::is_same_v<list::at_t<2>, int>);
被编译了?因为索引为 2 的参数是 i
,它是未加括号的 id 表达式(我错了吗?),那么根据 C++17 标准 decltype(i)
应该是由 i
命名的实体的类型。我们可以清楚地看到,类型是 const int
(不是 int
),因为 constexpr
意味着 const
。但同时我怀疑所有三个主要编译器都可能是错误的。相反,我没有考虑某些事情,这是另一个问题的主题。