2

我是 C++ 元编程的新手,几天前我决定用可变参数包编写一个模板,它知道参数包中的哪个位置首先出现int。更准确地说,我想要一个structwith nameGetIntPos和 with static 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;

如何做呢?

4

5 回答 5

4

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

#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<std::size_t>(-1). 1

由于您提到使用0for not found 进行 1-indexed,因此 usingfind_type<int, Types...>::value + 1将产生0if not found (由于溢出)或1-indexed 结果 - 根据要求。


1未明确定义为 1-indexed 的原因是,当前定义可以重用于在可变参数包中查找任何类型的索引 - 并且大多数使用包含可变参数的类型的接口都期望 0-indexing(例如作为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
于 2020-09-22T19:12:35.823 回答
4

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

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
于 2020-09-22T19:12:45.937 回答
3

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

#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 开始工作。

于 2020-09-22T19:46:28.327 回答
2

我的递归方式

#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 开始工作。

于 2020-09-22T19:19:11.917 回答
0

这是使用 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>());
}
于 2020-10-21T03:16:12.343 回答