【问题标题】:Copy-assigning to a tuple from an iterator range in C++17从 C++17 中的迭代器范围复制分配给元组
【发布时间】:2021-05-27 03:15:37
【问题描述】:

我一直希望使用新的 C++17 功能(如折叠表达式和 std::apply())来简化我拥有的一些 C++11 代码,这些代码使用 std::index_sequencestd::initializer_list 等工具对元组进行一些操作。特别是一项任务给我带来了一些麻烦:将一系列值(例如,从boost::tokenizer 对象)复制到一个元组。我有一个工作解决方案,它在要为其分配值的元组上调用 std::apply(),但它仍然必须在内部使用 std::initializer_list(为简单起见,我用一个简单的向量替换了 boost tokenizer 迭代器:

#include <iostream>
#include <vector>
#include <tuple>

template<typename Tuple, typename Iterator>
size_t copy_range_to_tuple(Tuple& tup, Iterator begin, Iterator end) {
    size_t count = 0;
    auto copy = [&begin,&end,&count] (auto& value) -> bool {
        if (begin != end) {
            value = *(begin++);
            ++count;
            return true;
        } else {
            return false;
        }
    };
    std::apply([&copy](auto&... values) {
        std::initializer_list<bool>{copy(values)...};
    }, tup);
    return count;
}

int main(int,char**) {
    std::tuple<int,int,int,int> tup;
    std::vector<int> x{4,3,2};
    std::cout << "copy count = " << copy_range_to_tuple(tup, x.begin(), x.end()) << std::endl;
    std::cout << std::get<0>(tup) << std::endl;
    std::cout << std::get<1>(tup) << std::endl;
    std::cout << std::get<2>(tup) << std::endl;
    std::cout << std::get<3>(tup) << std::endl;
    
}

输出:

copy count = 3
4
3
2
0

与我的 C++11/14 代码相比,这已经是一个巨大的胜利,我的 C++11/14 代码(由于缺少 std::apply())使用索引序列将迭代器与不同的元组元素匹配。但是,我仍然有一种感觉或希望,这可以通过折叠表达式进一步简化,从而消除对初始化列表的需要。准确地说,我希望我可以制作一个扩展copy(values) 的折叠表达式。这可能吗,或者现代 C++ 是否有其他技巧可以减少冗长? (摆脱 copy lambda 也很好,但我认为为了检查有效的迭代器是不可避免的)。

【问题讨论】:

  • 您是否经常使用具有统一类型的元组? std::array 可能更适合。
  • @MarcGlisse 不,为了简单起见,我只是制作了演示示例,但感谢您的建议。由于 std::apply 可以接受 std::array 参数,我猜上述方法可以通用

标签: c++ iterator tuples c++17 fold-expression


【解决方案1】:

带有折叠表达式的版本可能如下所示:

std::tuple<int,int,int,int> t;
std::vector<int> v{4,3};
auto beg = v.begin();
auto end = v.end();
std::apply([&](auto&... values){
    auto getValue = [&](){ return (beg != end) ? *beg++ : 0; }; // returns 0 if range is too small
    ((values = getValue()),...);
}, t);
std::cout << std::get<0>(t) << std::endl; // 4
std::cout << std::get<1>(t) << std::endl; // 3
std::cout << std::get<2>(t) << std::endl; // 0
std::cout << std::get<3>(t) << std::endl; // 0

Demo

【讨论】:

    【解决方案2】:

    我想你可以简单地使用迭代器并调用std::apply();我是说

    auto it = v.begin();
    
    std::apply([&](auto & ... values)
              { ((it != v.end() ? values = *it++ : values), ... ); }, t); 
    

    以下是完整的编译示例

    #include <tuple>
    #include <vector>
    #include <iostream>
    
    int main ()
     {
       std::tuple<int,int,int,int> t;
       std::vector<int> v{4,3,2};
       auto it = v.begin();
    
       std::apply([&](auto & ... values)
                 { ((it != v.end() ? values = *it++ : values), ... ); }, t);
    
       std::cout << std::get<0>(t) << std::endl; // 4
       std::cout << std::get<1>(t) << std::endl; // 3
       std::cout << std::get<2>(t) << std::endl; // 2
       std::cout << std::get<3>(t) << std::endl; // 0
     }
    

    【讨论】:

    • 感谢您的回答!值得注意的是,这可以简洁地完成,无需复制 lambda 函数(并且 std::distance 可用于获取复制元素的数量)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-14
    • 1970-01-01
    • 2021-05-30
    • 2014-06-11
    • 1970-01-01
    • 2022-12-07
    • 1970-01-01
    相关资源
    最近更新 更多