【问题标题】:C++ template that knows its argument's position知道其参数位置的 C++ 模板
【发布时间】:2021-01-08 22:51:08
【问题描述】:

我是 C++ 元编程的新手,几天前我决定用可变参数包编写一个模板,它知道参数包中的哪个位置首先出现int。更准确地说,我希望有一个名称为 GetIntPosstructstatic constexpr int value,它表示 int 在参数包中以 1 开头的位置,如果参数包中没有 int 参数类型,则为 0。例如

cout<<GetIntPos<long, char, int, long>::value; // must print 3
cout<<GetIntPos<int, int, long>::value; // must print 1

cout<<GetIntPos<long, long, char>::value; // must print 0;

怎么做?

【问题讨论】:

  • 到目前为止你有什么尝试?
  • @StephenNewell 我试图用它的帮助模板编写结构。辅助模板是递归可变的,并且还获得一个 int 模板参数,该参数随着每次递归移动而增加,直到递归结束或找到 int。
  • @StephenNewell - 为什么?这个问题很清楚,有一个精确的问题陈述,并且可以在 SO 帖子中回答。无需将所有内容都变成调试会话。
  • StackOverflow 不是免费的编码服务。你应该try to solve the problem first。请更新您的问题以在minimal reproducible example 中显示您已经尝试过的内容。如需更多信息,请参阅How to Ask,并拨打tour :)
  • @StoryTeller-UnslanderMonica - 我的理解是我们应该看到尝试,而不仅仅是提供解决方案。

标签: c++ metaprogramming variadic-templates template-meta-programming parameter-pack


【解决方案1】:

这实际上很容易用模板类型特征解决:

#include <type_traits> // std::integral_constant
#include <cstddef>     // std::size_t

// Recursive base template (specialized below)
template <std::size_t Current, typename T, typename...Types>
struct find_type_aux;

// Specialization: If we find a match
template <std::size_t Current, typename T, typename...Types>
struct find_type_aux<Current,T,T,Types...>
  : std::integral_constant<std::size_t,Current>
{};

// Specialization: If we haven't found a match, but still have some left in the pack
template <std::size_t Current, typename T, typename Type0, typename...Types>
struct find_type_aux<Current,T,Type0, Types...>
  : find_type_aux<Current + 1, Types...> // Strip off first, and search rest. Increment count
{};

// Specialization: If we didn't find a match
template <std::size_t Current, typename T>
struct find_type_aux<Current,T>
  : std::integral_constant<std::size_t,static_cast<std::size_t>(-1)>{};
{};

// The interface: Find's "T" and returns the 0-based index.
template <typename T, typename...Types>
struct find_type : find_type_aux<0u,T,Types...>{};

使用这样的特征,找到的元素将是从 0 开始的索引,而未找到的元素将是 static_cast&lt;std::size_t&gt;(-1)1

由于您提到使用 0 进行 1 索引以表示未找到,因此使用 find_type&lt;int, Types...&gt;::value + 1 将产生 0 如果未找到(由于溢出)或 1-indexed 结果 - 根据要求。


1 没有明确定义为 1-indexed 的原因是,当前定义可以重用以查找可变参数包中任何类型的索引 - 以及大多数操作的接口包含可变参数的类型需要 0 索引(例如 std::get)。这可以很容易地用作专门针对int 的构建块,例如使用变量模板:

template <typename...Types>
constexpr auto find_int_v = find_type<int,Types...>::value + 1;

然后从以下位置产生正确答案:

int main() {
    std::cout << find_int_v<short, long, char> << '\n';
    std::cout << find_int_v<int, short, long, char> << '\n';
    std::cout << find_int_v<short, long, char, int> << '\n';
    return 0;
}

作为

0
1
4

【讨论】:

    【解决方案2】:

    这似乎在我的测试中有效:

    namespace detail {
        template <int INDEX>
        constexpr int GetIntPosImpl() {
            return 0;
        }
    
        template <int INDEX, typename T, typename ...Ts>
        constexpr int GetIntPosImpl() {
            if constexpr(std::is_same_v<T, int>) {
                return INDEX;
            }
            else {
                return GetIntPosImpl<INDEX + 1, Ts...>();
            }
        };
    }
    
    template <typename ...Ts>
    struct GetIntPos {
        static constexpr int value = detail::GetIntPosImpl<1, Ts...>();
    };
    
    int main() {
        std::cout << GetIntPos<short, long, char>::value << '\n';
        std::cout << GetIntPos<int, short, long, char>::value << '\n';
        std::cout << GetIntPos<short, long, char, int>::value << '\n';
        return 0;
    }
    

    我的输出:

    0
    1
    4
    

    【讨论】:

      【解决方案3】:

      我的递归方式

      #include <iostream>
      #include <type_traits>
      
      template <std::size_t, typename ...>
      struct GIP_helper
         : public std::integral_constant<std::size_t, 0u>
       { };
      
      template <std::size_t N, typename T0, typename ... Ts>
      struct GIP_helper<N, T0, Ts...>
         : public GIP_helper<N+1u, Ts...>
       { };
      
      template <std::size_t N, typename ... Ts>
      struct GIP_helper<N, int, Ts...>
         : public std::integral_constant<std::size_t, N>
       { };
      
      
      template <typename... Ts>
      struct GetIntPos : public GIP_helper<1u, Ts...>
       { };
      
      int main()
       {
         std::cout << GetIntPos<long, char, int, long>::value << std::endl; 
         std::cout << GetIntPos<int, int, long>::value << std::endl;
         std::cout << GetIntPos<long, long, char>::value << std::endl;
       }
      

      从 C++11 开始工作。

      【讨论】:

      • 感谢您的回答。我认为这是最直接的方法。
      • @VahagChakhoyan - 嗯……一个小建议:尽可能避免递归。还要考虑到递归步骤的数量存在限制(编译器细节,但可配置)。我的另一个答案更神秘(也许可以改进),但我认为更好。嗯......也许不是我的确切解决方案......但是这个想法。
      【解决方案4】:

      我的非递归方式(只是为了好玩......或代码混淆上下文)

      #include <iostream>
      #include <algorithm>
      #include <type_traits>
      
      template <int = 0>
      auto GIP_helper (std::index_sequence<>)
         -> std::integral_constant<std::size_t, 0u>;
      
      template <typename ... Ts, std::size_t ... Is>
      auto GIP_helper (std::index_sequence<Is...>)
         -> std::integral_constant<std::size_t,
             (1u+std::min({(std::is_same<Ts, int>::value
                            ? Is
                            : sizeof...(Is))...})) % (1u+sizeof...(Is))>;
      
      template <typename ... Ts>
      using GetIntPos
         = decltype( GIP_helper<Ts...>(std::index_sequence_for<Ts...>{}) );
      
      int main()
       {
         std::cout << GetIntPos<long, char, int, long>::value << std::endl; 
         std::cout << GetIntPos<int, int, long>::value << std::endl;
         std::cout << GetIntPos<long, long, char>::value << std::endl;
       }
      

      从 C++14 开始工作。

      【讨论】:

      • GetIntPos::value 必须为 0,但会出现编译错误。
      • @VahagChakhoyan - 好点。添加了GIP_helper() 的重载版本来管理特殊的空列表情况。
      【解决方案5】:

      这是使用 Boost.MP11 的解决方案:

      #include <boost/mp11/algorithm.hpp>
      
      template<class... TArgs>
      constexpr auto get_int_pos() {
          using list_t = boost::mp11::mp_list<TArgs...>;
          using index_t = boost::mp11::mp_find<list_t, int>;
          return index_t{} == boost::mp11::mp_size<list_t>{} ? 0 : 1 + index_t{};    
      }
      
      int main() {
          static_assert(0 == get_int_pos<double>());
          static_assert(0 == get_int_pos<long, long, char>());
          static_assert(1 == get_int_pos<int, int, long>());
          static_assert(3 == get_int_pos<long, char, int, long>());
          static_assert(4 == get_int_pos<short, long, char, int>());
      }
      

      【讨论】:

        猜你喜欢
        • 2015-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-18
        • 1970-01-01
        • 1970-01-01
        • 2023-03-25
        • 2015-10-16
        相关资源
        最近更新 更多