4

タプルのようなインターフェイスを持つカスタム クラスがあります。コードをできるだけ汎用的なものにしたいので、関数std::getstd::tuple_sizeに基づいてアルゴリズムを作成することをお勧めします。std::tuple_elementしたがって、これらの関数を特殊化してアルゴリズムを使用するだけで済みます。これらの関数の特殊化を必要とする概念を呼びましょうTuple

今、私は のコンポーネントを合計しようとしていますTuple。関数宣言は次のようにする必要があります。

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

多くのテンプレート プログラミングが関係していると思いますが、その方法がわかりません。

追加には、 global のオーバーロードを使用し+ operatorます。

私はc++1zを使用しています。

4

2 に答える 2

12

これは非常に簡単です。

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::refINVOKE

では、この時点でコンパイラをアップグレードすることをお勧めします。、この時点でジョブをアップグレードすることをお勧めします。


上記のすべては、右または左の折り畳みを行います。場合によっては、二分木フォールドの方が適している場合があります。これはよりトリッキーです。

式テンプレートを使用する場合+、上記のコードは有効期間の問題によりうまく機能しません。場合によっては、一時的な式ツリーを評価させるために、「afterwards, cast-to」用の別のテンプレート タイプを追加する必要がある場合があります。

于 2016-11-22T18:30:38.137 に答える
7

C++1z では、fold 式を使用すると非常に簡単です。まず、タプルを_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>{});
}

デモ


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

デモ

C++11 アプローチは、カスタム実装の C++14 アプローチになりますindex_sequence。たとえばここから。


@ildjarn がコメントで指摘したように、上記の例は両方とも右折畳みを採用していますが、多くのプログラマーはコードで左折畳みを期待しています。C++1z バージョンは簡単に変更できます。

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

デモ

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

デモ

于 2016-11-20T02:56:37.810 に答える