如果constexpr具有递归参数包

时间:2019-06-05 04:01:55

标签: c++

我试图递归地遍历类型列表,以便我可以根据列表中的每种类型执行一些运行时代码。我希望能够在不使用“ if constexpr”来终止递归的情况下,以递归方式遍历结构体中函数的元组中的所有类型(而不是结构体中的函数)。

我有一小段代码,展示了使用constexpr进行递归操作。

 @Document(collection = "user")
 public class User extends AbstractEntity{

       private String name;

       private String lastName;

       private ????? address

       ...
  }

我希望能够使用C ++ 14(而不是带有“ if constexpr”的C ++ 17)进行相同的递归。

谢谢!

4 个答案:

答案 0 :(得分:7)

诀窍是使用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();
}

答案 1 :(得分:5)

如果您可以将_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(),而是变为未命名临时变量的声明,并调用执行输出的构造函数。

答案 2 :(得分:4)

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

您应该使用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. 如果您需要加入私有成员资格,则需要将*this传递给通话,并将新的struct添加为班级的朋友
  2. 在我的代码中,我在/* other dynamic code */部分上有一个重复项。你可以 :
    • 使用功能
    • 将模板参数设为int而不是size_t,然后停在-1而不是0

PS:

我没有得到部分

  

结构体中函数的元组中(而不是结构体中的函数)

我希望我不会错过任何事情

编辑

我的代码比您的代码执行了更多的迭代,您可以将其清空:

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

您不会在0情况下显示。

答案 3 :(得分: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>{} );
}

这种小小的美丽给了您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>{} );
}

Live example

请注意,我们必须通过lambda的参数传递N,因为我们希望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参数。

Live example