从模板模板方法参数中获取类型

Getting type from template template method parameters

本文关键字:获取 取类型 参数 模板方法      更新时间:2023-10-16

我试图使一种方法,从容器类型中选择一个随机元素,如std::vector。之前,我用的是:

std::string pick_random(std::vector<std::string> c) {
    int r = std::rand() % ids.size() + 1;
    auto it = c.begin();
    std::advance(it, r);
    return *it;
}

,据我所知,它工作得很好。这并不是说就是好,只是它看起来就是

我很快就不得不为另一个容器做同样的事情,所以我尝试使用模板模板参数使方法泛型:

template <template<typename element_t> container_t>
element_t pick_random(container_t from) { /* ... */ }
但是,这会抛出一个错误:
element_t does not name a type

我认为我的意图是足够清楚的,但重申一下:我试图获得列表的元素类型。我可以有一个单独的模板参数,但这样它就不能正确地推断类型。我尝试了各种不同的版本,但没有一个工作。

container_t不是类型,container_t<T>是。

您可以使用以下命令:

template <template<typename, typename...> C, typename T, typename...Ts>
T pick_random(const C<T, Ts...>& from);

对于std::vector,您有一个分配器:std::vector<T, Alloc>

在c++ 14中,可以简单地使用auto

template <typename C>
auto pick_random(const C& from) { /* ... */ }

我不认为这里需要"模板模板参数",您可以简单地使用容器中的value_type:

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include <list>
template <typename T>
typename T::value_type pick_random(T& from) {
    int r = std::rand() % from.size();
    auto it = from.begin();
    std::advance(it, r);
    return *it;
}
int main() {
    std::srand(std::time(0));
    std::vector<std::string> words {"the", "frogurt", "is", "also", "cursed"};
    std::list<int> numbers {1, 2, 3, 4, 5};
    std::cout << "words: "   << pick_random(words)   << std::endl;
    std::cout << "numbers: " << pick_random(numbers) << std::endl;
}

value_type——通过对迭代器解引用可获得的值的类型。

来源:http://en.cppreference.com/w/cpp/iterator/iterator_traits

最好是避免对类模板的任意限制。毕竟,为什么不能从原始数组中选择一个元素呢?为了在c++ 11中正确命名类型,我们必须获得对begin的非限定调用的结果,我们可以通过以下方式获得:

namespace detail {
    using std::begin;
    template <typename C>
    auto adl_begin(C&& c) -> decltype(begin(std::forward<C>(c))) {
        return begin(std::forward<C>(c));
    }
}
using detail::adl_begin;

然后使用从任意容器中推导出element_type:

template <typename C>
auto pick_random(C& container) -> decltype(*adl_begin(container))
{ /* rest as before */ }

旁注:通过引用而不是通过值来获取容器。

如果您只使用标准库容器,那么您可以通过使用container_t::value_type来获取存储类型。

template <typename container_t>
typename container_t::value_type pick_random(container_t& container)
{ ... }