2

我有这段代码,我正在尝试使用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技巧来让它工作?

4

2 回答 2

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) );
    }
};

如果你想添加一个 clearer ,你可以通过添加一个重载来做到这一点,该重载仅在超出范围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");
    }

这是一个活生生的例子

于 2013-05-03T19:53:38.467 回答
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>();
}

(抛出异常是没有意义的,因为异常是针对*异常运行时流控制的,而不是针对静态编译错误检查的。)

于 2013-05-03T19:43:35.160 回答