【问题标题】:Sum the components of a tuple up by using std::get, std::tuple_size, std::tuple_element使用 std::get、std::tuple_size、std::tuple_element 对元组的组件求和
【发布时间】:2016-11-20 02:48:44
【问题描述】:

我有一个自定义类,它有一个类似元组的接口。因为我希望我的代码尽可能通用,所以我认为将我的算法基于函数std::getstd::tuple_sizestd::tuple_element 是一个好主意,所以你只需要专门化这些函数来使用我的算法。我们将需要这些函数特化的概念称为Tuple

现在我试图总结Tuple 的组成部分。函数声明应该是这样的:

template <class Tuple>
int sum_components(const Tuple& t);

我想这涉及到很多模板编程,但我就是不知道该怎么做。

另外,我将只使用全局 + operator 的重载。

我正在使用 c++1z。

【问题讨论】:

    标签: c++ tuples c++17 stdtuple boost-tuples


    【解决方案1】:

    这在 中很容易。

    template<class Tuple>
    decltype(auto) sum_components(Tuple const& tuple) {
      auto sum_them = [](auto const&... e)->decltype(auto) {
        return (e+...);
      };
      return std::apply( sum_them, tuple );
    };
    

    (...+e) 表示相反的折叠方向。

    在以前的版本中,正确的方法是编写自己的apply,而不是编写定制的实现。当您的编译器更新时,您可以删除代码。

    ,我可能会这样做:

    // namespace for utility code:
    namespace utility {
      template<std::size_t...Is>
      auto index_over( std::index_sequence<Is...> ) {
        return [](auto&&f)->decltype(auto){
          return decltype(f)(f)( std::integral_constant<std::size_t,Is>{}... );
        };
      }
      template<std::size_t N>
      auto index_upto() {
        return index_over( std::make_index_sequence<N>{} );
      }
    }
    // namespace for semantic-equivalent replacements of `std` code:
    namespace notstd {
      template<class F, class Tuple>
      decltype(auto) apply( F&& f, Tuple&& tuple ) {
        using dTuple = std::decay_t<Tuple>;
        auto index = ::utility::index_upto< std::tuple_size<dTuple>{} >();
        return index( [&](auto...Is)->decltype(auto){
          auto target=std::ref(f);
          return target( std::get<Is>( std::forward<Tuple>(tuple) )... );
        } ); 
      }
    }
    

    这与 中的std::apply 非常接近。 (我滥用std::ref 来获取INVOKE 语义)。 (它不能完美地与右值调用程序一起工作,但这是非常极端的情况)。

    中,我建议此时升级您的编译器。在,我建议此时升级你的工作。


    以上所有方法都进行左右折叠。在某些情况下,二叉树折叠可能会更好。这更棘手。

    如果您的+ 使用表达式模板,则上述代码由于生命周期问题而无法正常运行。在某些情况下,您可能必须为“之后,强制转换”添加另一种模板类型以使临时表达式树进行评估。

    【讨论】:

    • 只需要一些decltype(auto) 洒水。
    • @T.C.添加到味道。最初省略它们是因为我不想考虑表达式模板和其他混乱。从临时自动存储对象的字段返回为T&amp;&amp; 的中间结果极有可能与上述情况发生冲突。但现在我认为这是表达式模板的问题。
    • 非常感谢。这个解决方案甚至比@krzaq 的更好,因为您使用了我以前不知道的std::apply。我接受了你的解决方案。
    • 在 C++03 中,我建议升级你的工作。说得好:o)
    【解决方案2】:

    对于 C++1z,使用 fold expressions 非常简单。首先,将元组转发到_impl 函数并为其提供索引序列以访问所有元组元素,然后求和:

    template<typename T, size_t... Is>
    auto sum_components_impl(T const& t, std::index_sequence<Is...>)
    {
        return (std::get<Is>(t) + ...);
    }
    
    template <class Tuple>
    int sum_components(const Tuple& t)
    {
        constexpr auto size = std::tuple_size<Tuple>{};
        return sum_components_impl(t, std::make_index_sequence<size>{});
    }
    

    demo


    C++14 方法是递归地对可变参数包求和:

    int sum()
    {
        return 0;
    }
    
    template<typename T, typename... Us>
    auto sum(T&& t, Us&&... us)
    {
        return std::forward<T>(t) + sum(std::forward<Us>(us)...);
    }
    
    template<typename T, size_t... Is>
    auto sum_components_impl(T const& t, std::index_sequence<Is...>)
    {
        return sum(std::get<Is>(t)...);
    }
    
    template <class Tuple>
    int sum_components(const Tuple& t)
    {
        constexpr auto size = std::tuple_size<Tuple>{};
        return sum_components_impl(t, std::make_index_sequence<size>{});
    }
    

    demo

    C++11 方法是自定义实现 index_sequence 的 C++14 方法。例如来自here


    正如@ildjarn 在 cmets 中指出的那样,上述示例都使用了右折叠,而许多程序员希望在他们的代码中使用左折叠。 C++1z 版本是微不足道的:

    template<typename T, size_t... Is>
    auto sum_components_impl(T const& t, std::index_sequence<Is...>)
    {
        return (... + std::get<Is>(t));
    }
    

    demo

    C++14 也差不了多少,但变化更多:

    template<typename T, typename... Us>
    auto sum(T&& t, Us&&... us)
    {
        return sum(std::forward<Us>(us)...) + std::forward<T>(t);
    }
    
    template<typename T, size_t... Is>
    auto sum_components_impl(T const& t, std::index_sequence<Is...>)
    {
        constexpr auto last_index = sizeof...(Is) - 1;
        return sum(std::get<last_index - Is>(t)...);
    }
    

    demo

    【讨论】:

    • 有趣的是,您在 C++14 中使用了左折叠,而在 C++17 中使用了右折叠。 ;-]
    • @ildjarn 好点。我不是有意识地这样做,但我认为这是因为我本能地想将“已知”元素保留在左侧。但是,如果他的 op+ 不是关联的或可交换的,那么 OP 就会有另一个问题;)
    • 同意,不应该sum很重要(虽然+不一定安全——std::string),但总的来说,我认为那些正在第一次接触折叠表达式应该显示左折叠,因为这是大多数 C++ 程序员所期望的(任何具有 FP 背景的人都不会被愚弄,只要他们知道两者都是可能的)。不是对你的回答的批评,只是一个观察。 :-D
    • @ildjarn 批评与否,已更新。现在的例子应该是一致的。我感谢您的反馈,希望我能记住“LHS @ REST”的心理模型并不是最好的方法:)
    • @krzaq:链接错误?假设您的意思是this,它似乎可以解决问题。 :-D 终止案例是明确输入的,我不喜欢;修复将涉及std::common_type 某处加上额外的模板参数来传递它 - 哎呀。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-04
    • 2017-07-31
    • 1970-01-01
    • 2020-07-07
    • 1970-01-01
    相关资源
    最近更新 更多