简体   繁体   English

返回与C ++ 17中可变参数模板相对应的元组

[英]Return a tuple corresponding to variadic template in C++17

How to return a tuple from a function that takes a list of variadic template? 如何从采用可变参数模板列表的函数返回元组? The tuple types should be the same to the argument types. 元组类型应与参数类型相同。

template <typename ... T, typename D>
std::tuple<T...> foo(D&& Duration) {
  // How to do this?
  if constexpr(sizeof... (args) > 0) {
    ... 
  }
  // What to return?
}

The idea is to use C++17 structure binding so I can do something like this: 这个想法是使用C ++ 17结构绑定,所以我可以做这样的事情:

auto var = std::chrono::seconds(19874);

auto [h, m] = foo<std::chrono::hours, std::chrono::minutes> (var);
auto [m, s, ms] = foo<std::chrono::minutes, std::chrono::seconds, std::chrono::milliseconds>(var);

For both cases, the sum (h:m or m:s:ms) should be 19874 seconds. 对于这两种情况,总和(h:m或m:s:ms)应为19874秒。

This code should do what you ask. 此代码应执行您的要求。 It constructs the tuple using an initializer list of calls to chrono_extract for each of the ouptut types. 它使用对每种chrono_extract类型的chrono_extract调用的初始化列表构造元组。 The initializer list parameters are processed in order according to the standard, so this shouldn't be sensitive to any reordering even though it might look like it from the use of the commas. 初始化程序列表参数是根据标准按顺序处理的,因此即使使用逗号看起来像这样,它也不会对任何重新排序敏感。 The chrono_extract function casts the input duration to the type of the output duration, then subtracts the output from the input (which is passed by reference, so its value will be reflected in chrono_components ). chrono_extract函数将输入持续时间强制转换为输出持续时间的类型,然后从输入中减去输出(该输出通过引用传递,因此其值将反映在chrono_components )。 In order to correctly run the algorithm, we need to find the smallest time component in the list, and run all calculations at the smallest resolution (otherwise there will be duration casting errors). 为了正确地运行算法,我们需要在列表中找到最小的时间分量,并以最小的分辨率运行所有计算(否则将存在持续时间转换错误)。 Also provided is an assertion to make sure that the return duration types are in decreasing order because the function will not operate correctly if they are in a different order. 还提供了一个断言,以确保返回持续时间类型按递减顺序排列,因为如果函数按不同顺序排列将无法正确运行。 One possible enhancement that could be added is to also return a "remainder" in the tuple (that is the same as the input type) if there is anything left over from the decomposition (as in the first test case in the code below). 可以添加的一个可能的增强功能是,如果分解中还剩下任何内容,则在元组中也返回一个“余数”(与输入类型相同)(如下面代码中的第一个测试用例)。

#include <chrono>
#include <iostream>
#include <tuple>
#include <type_traits>

template <typename lhs_t, typename rhs_t, typename... other_ts>
constexpr void assert_decreasing_ratios() {
    static_assert(std::ratio_greater_v<typename lhs_t::period,
                                       typename rhs_t::period>,
                  "Periods are non-decreasing.");
    if constexpr (sizeof...(other_ts)) {
        assert_decreasing_ratios<rhs_t, other_ts...>();
    }
}

template <typename return_duration_t, typename input_duration_t>
return_duration_t chrono_extract(input_duration_t& value) {
    auto extracted = std::chrono::duration_cast<return_duration_t>(value);
    value -= extracted;
    return extracted;
}

template <typename... return_ts, typename duration_t>
std::tuple<return_ts...> chrono_components(duration_t value) {
    assert_decreasing_ratios<return_ts...>();
    using smallest_t = std::tuple_element_t<sizeof...(return_ts) - 1,
                                            std::tuple<return_ts...>>;
    auto small_value = std::chrono::duration_cast<smallest_t>(value);
    return {chrono_extract<return_ts>(small_value)...};
}

int main()
{
    std::chrono::seconds before(19874);

    {
        auto [h, m] = chrono_components<std::chrono::hours,
                                        std::chrono::minutes>(before);
        std::chrono::seconds after(h + m);
        std::cout << h.count() << " hours " 
                  << m.count() << " minutes = "
                  << after.count() << " seconds\n";
    }

    {
        auto [m, s, ms] = chrono_components<std::chrono::minutes,
                                            std::chrono::seconds,
                                            std::chrono::milliseconds>(before);
        auto after =
            std::chrono::duration_cast<std::chrono::seconds>(m + s + ms);
        std::cout << m.count() << " minutes " 
                  << s.count() << " seconds " 
                  << ms.count() << " milliseconds = "
                  << after.count() << " seconds\n";
    }
}

Output 输出量

5 hours 31 minutes = 19860 seconds
331 minutes 14 seconds 0 milliseconds = 19874 seconds

The first line shows that the seconds value has been truncated because it's not an exact number of minutes. 第一行显示秒数值已被截断,因为它不是精确的分钟数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM