我有这个代码,我正在尝试使用std::get
获得对元组的编译时访问。当用户输入一个超出元组范围的数字时,我想抛出异常。不幸的是,我不能让它工作,所以我让代码返回数组中的第一个元素,如果它超出范围。
#include <tuple>
template <class... T>
struct input
{
std::tuple<T...> var;
input(T&&... t) : var(std::forward<T>(t)...) {}
template <
std::size_t N,
bool in_range = 0 <= N && N < std::tuple_size<decltype(var)>::value
>
auto get()
-> typename std::tuple_element<in_range ? N : 0, decltype(var)>::type&&
{
return std::move( std::get<in_range ? N : 0>(var) );
}
};
template <class... Args>
void f(Args&&... args)
{
auto arguments = input<Args...>(std::forward<Args>(args)...);
arguments.template get<9>(); // returns 2 but I'd rather throw an exception
}
int main()
{
f(2, 4, 6, 8);
}
如何抛出异常或至少使用static_assert
技巧来实现此功能?
答案 0 :(得分:3)
异常用于报告运行时错误,而在此处处理编译时编程问题。
如果您介意编译器会尝试使用越界索引实例化tuple_element<>
时出现的非常非常透明的错误,您可以使用SFINAE来防止您的函数被实例化在重载决策期间:
template <class... T>
struct input
{
std::tuple<T...> var;
input(T&&... t) : var(std::forward<T>(t)...) {}
template<std::size_t N,
typename std::enable_if<(N < std::tuple_size<decltype(var)>::value)>::
type* = nullptr>
auto get() -> typename std::tuple_element<N, decltype(var)>::type&&
{
return std::move( std::get<N>(var) );
}
};
如果你想添加一个更清晰的static_assert()
,你可以通过添加一个只在N
超出范围时才被选中的重载来实现:
template<std::size_t N,
typename std::enable_if<(N >= std::tuple_size<decltype(var)>::value)>::
type* = nullptr>
void get()
{
static_assert(N < std::tuple_size<decltype(var)>::value, "OOB");
}
这是live example。
答案 1 :(得分:2)
试试这个:
template <class... Args>
void f(Args&&... args)
{
static_assert(sizeof...(Args) > 9, "Invalid tuple size");
auto arguments = input<Args...>(std::forward<Args>(args)...);
arguments.template get<9>();
}
(抛出异常没有意义,因为异常是针对*异常运行时流控制的“,而不是针对静态编译错误检查。)