如果带有递归参数包的 constexpr



>我正在尝试递归地运行类型列表,以便我可以根据列表中的每种类型执行一些运行时代码。我希望能够在结构中的函数(而不是结构中的函数(中的元组中递归运行所有类型,而无需使用"if constexpr"来终止递归。

我有一段代码显示了使用 constexpr 的递归。

#include <iostream>
#include <string>
#include <tuple>
template <typename ...Ts>
struct temp{
    using TypeList = std::tuple<Ts...>;
    constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
    void print_this()
    { 
        _inner_print<_N,_N>();
    }
    template <std::size_t N, std::size_t MAX>
    void _inner_print()
    {
        if constexpr ( N != 0 )
        {
            std::cout << "Call #"<<MAX-N<<std::endl;
            ////////////////////////
            /* other dynamic code */
            ////////////////////////
            _inner_print<N-1, MAX>();
        }
    }
    TypeList _mem;
};
int main()
{
    std::string name;
    temp<int, int, int> t1;
    t1.print_this();
}

我希望能够对 C++14 执行相同的递归,而不是 C++17 w/"if constexpr"。

谢谢!

诀窍

是使用index_sequence

这是一个 C++14 的工作解决方案,使用@MartinMorterol建议进行了改进。

// -*- compile-command: "g++ -Wall -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
{
  using TypeList = std::tuple<Ts...>;
  constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
  void print_this() { _inner_print(std::make_index_sequence<_N>()); }
  template <std::size_t... IDX>
  void _inner_print(std::index_sequence<IDX...>)
  {
    auto dummy = {0, (_inner_print<IDX>(),0)...};
    (void)dummy;
  }
  template <std::size_t IDX>
  void _inner_print()
  {
    std::cout << "nCall #" << IDX 
              << " sizeof " << sizeof(std::get<IDX>(_mem));
  }
  TypeList _mem;
};
int main()
{
  std::string name;
  temp<int, double, char> t1;
  t1.print_this();
}

其中打印:

g++ -Wall -std=c++14 poub.cpp; ./a.out
Call #0 sizeof 4
Call #1 sizeof 8
Call #2 sizeof 1

我的初始答案(使用递归(

// -*- compile-command: "g++ -std=c++14 poub.cpp; ./a.out"; -*-
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
template <typename... Ts>
struct temp
{
  using TypeList = std::tuple<Ts...>;
  constexpr static std::size_t _N = std::tuple_size<TypeList>::value;
  void print_this() { _inner_print(std::make_index_sequence<_N>()); }
  template <std::size_t... IDX>
  void _inner_print(std::index_sequence<IDX...>)
  {
    _inner_print(std::integral_constant<std::size_t, IDX>()...);
  }
  template <std::size_t HEAD_IDX, typename... TAIL>
  void _inner_print(std::integral_constant<std::size_t, HEAD_IDX>, TAIL... tail)
  {
    std::cout << "nCall #" << HEAD_IDX 
              << " sizeof " << sizeof(std::get<HEAD_IDX>(_mem));
    // whatever you want HERE ...
    _inner_print(tail...);
  }
  void _inner_print(){};
  TypeList _mem;
};
int main()
{
  std::string name;
  temp<int, double, char> t1;
  t1.print_this();
}

如果可以将_inner_print函数更改为类,则可以使用部分专用化来结束递归:

template <std::size_t N, std::size_t MAX>
struct _inner_print
{
    _inner_print()
    {
        std::cout << "Call #"<<MAX-N<<std::endl;
        ////////////////////////
        /* other dynamic code */
        ////////////////////////
        _inner_print<N-1, MAX>();
    }
};
template <std::size_t MAX> struct _inner_print<0, MAX> { };

它不是将_inner_print()作为函数调用,而是成为未命名临时的声明,调用执行输出的构造函数。

您应该使用部分专用化。但是你不能用函数来做到这一点。

您应该使用struct来解决问题。

#include <iostream>
#include <string>
#include <tuple>
template <std::size_t N, std::size_t MAX, class T>
struct inner_print_impl{
        static void run(const T&  caller)
        {
            std::cout << "Call #"<<MAX-N<<  " " << caller.a << std::endl;
            ////////////////////////
            /* other dynamic code */
            ////////////////////////
            inner_print_impl<N-1, MAX , T>::run(caller);
        }
 };
template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX ,  T>{
        static void run(const T&  caller)
        {
            std::cout << "Call #"<<MAX<<  " " << caller.a << std::endl;
            ////////////////////////
            /* other dynamic code */
            ////////////////////////
            // no recursion
        }
 };

template <typename ...Ts>
struct temp{
    using TypeList = std::tuple<Ts...>;
    constexpr static std::size_t N_ = std::tuple_size<TypeList>::value;
    template <std::size_t N, std::size_t MAX, class T>
    friend struct inner_print_impl;
    void print_this()
    { 
        inner_print_impl<N_,N_, temp<Ts...> >::run(*this);
    }
    TypeList _mem;
    private : 
        int a ; // test acces
};
int main()
{
    std::string name;
    temp<int, int, int> t1;
    t1.print_this();
}

注意:

  1. 如果您需要访问私人 membre,则需要将*this传递给呼叫并将新struct添加为班级的朋友
  2. 在我的代码中,我在/* other dynamic code */部分有一个重复项。您可以 :
    • 使用函数
    • 将模板参数设置为int而不是size_t,并在-1而不是0处停止

附言 :

我不明白这个部分

在结构中的函数中的

元组中(不在结构中的函数中(

我希望我没有错过什么

编辑

我的代码比你的代码多做一次迭代,你可以清空这个:

template < std::size_t MAX, class T>
struct inner_print_impl<0, MAX ,  T>{
        static void run(const T&  caller)
        {
        }
 };

而且您不会显示0案例。

template<class Integral, Integral N>
auto dispatch( std::integral_constant<Integral, N> ) {
  return [](auto&&...args){
    return std::get<N>( std::forward_as_tuple( decltype(args)(args)... ) );
  };
}
template<std::size_t N>
auto dispatch() {
  return dispatch( std::integral_constant<std::size_t, N>{} );
}

这个小美女给你带来了 C++14 中if constexpr的魔力。

template <std::size_t N, std::size_t MAX>
void _inner_print() {
  dispatch( std::integral_constant<bool, N==0>{} )
  (
    // 0, aka false branch:
    [&](auto Nval){
      std::cout << "Call #"<<MAX-Nval<<std::endl;
      ////////////////////////
      /* other dynamic code */
      ////////////////////////
      _inner_print<Nval-1, MAX>();
    },
    // 1, aka true branch:
    [](auto&&){}
  )( std::integral_constant<std::size_t, N>{} );
}

活生生的例子。

请注意,我们必须通过参数将N传递给 lambda,因为我们希望 lambda 主体的有效性根据其参数而变化,而参数仅传递给正确的参数。


或者,您可以使用overload帮助程序:

template<class T0, class...Ts>
struct overloaded;
template<class Lhs, class Rhs, class...Ts>
struct overloaded<Lhs, Rhs, Ts...>:
  overloaded<Lhs>, overloaded<Rhs,Ts...>
{
  using overloaded<Lhs>::operator();
  using overloaded<Rhs,Ts...>::operator();
  template<class A0, class...As>
  explicit overloaded(A0&&a0, As&&...as) :
    overloaded<Lhs>(std::forward<A0>(a0)),
    overloaded<Rhs,Ts...>( std::forward<As>(as)... )
  {}
  overloaded(overloaded const&)=default;
  overloaded(overloaded &&)=default;
  overloaded& operator=(overloaded const&)=default;
  overloaded& operator=(overloaded &&)=default;
};
template<class T0>
struct overloaded<T0> : T0 {
  using T0::operator();
  template<class A0>
  explicit overloaded(A0&&a0):
    T0(std::forward<A0>(a0))
  {}
  overloaded(overloaded const&)=default;
  overloaded(overloaded &&)=default;
  overloaded& operator=(overloaded const&)=default;
  overloaded& operator=(overloaded &&)=default;
};
template<class R, class...Args>
struct overloaded<R(*)(Args...)> {
  R operator()(Args... args) const {
    return pf(std::forward<Args>(args)...);
  }
  R(*pf)(Args...);
  overloaded( R(*f)(Args...) ):pf(f) {}
  overloaded(overloaded const&)=default;
  overloaded(overloaded &&)=default;
  overloaded& operator=(overloaded const&)=default;
  overloaded& operator=(overloaded &&)=default;
};
template<class...Args>
auto overload( Args&&...args ) {
  return overloaded<std::decay_t<Args>...>(
    std::forward<Args>(args)...
  );
}

以下是如何使用overloaded

template <std::size_t N, std::size_t MAX>
void _inner_print()
{
  std::integral_constant<std::size_t, N> index;
  overload(
    [](std::integral_constant<std::size_t, 0>) {},
    [&](auto index) {
      std::cout << "Call #"<<MAX-index<<std::endl;
      ////////////////////////
      /* other dynamic code */
      ////////////////////////
      _inner_print<index-1, MAX>();
    }
  )(index);
}

请注意,规则是有时仅编译的动态代码必须依赖于 Lambda 的auto参数。

活生生的例子。

最新更新