【问题标题】:How to write a function that takes a "nested" template type as a parameter如何编写一个将“嵌套”模板类型作为参数的函数
【发布时间】:2021-05-23 13:42:13
【问题描述】:

我想编写一个通用函数,它采用一些类似地图的类型,并返回地图中的所有键。我还希望函数的 API 非常简单,像这样:

std::unordered_map<int, std::string> stringTable;
auto stringTableKeys = keys(stringTable); // should return a std::vector<int>

std::map<std::string, double> doubleTable;
auto doubleTableKeys = keys(doubleTable); // should return a std::vector<std::string>

我不希望它被绑定到 std::map 或 std::unordered_map,我也不希望被绑定到特定的键或值类型,所以我尝试编写以下内容:

template <typename Key, typename Value, template <typename, typename...> typename Table>
std::vector<Key> keys(const Table<Key, Value>& table)
{
    std::vector<Key> keys;
    keys.reserve(table.size());
    for (const auto& [key, value] : table)
        keys.push_back(key);
    return keys;
}

但是,如果我想使用这个版本的 keys() 函数,我必须这样调用它

auto stringTableKeys = keys<int, std::string, std::unordered_map>(stringTable);

如何在keys()的定义中指定模板,让调用者不必指定类型?

【问题讨论】:

  • 如果您的函数已经依赖于假设您编写的基于范围的for 循环可以正常工作,我不确定使用key_type 会失去更多的通用性.例如。只需将MapType 作为类模板参数并返回std::vector&lt;MapType::key_type&gt;
  • 我错过了什么吗?你的尝试似乎是work?

标签: c++ c++17


【解决方案1】:

抱歉,我的代码确实有效。 https://onlinegdb.com/By6hgZkMd

这只是 Visual Studio 2019 中的一个错误,我不确定它是否会起作用,因为它说这是一个语法错误:

【讨论】:

  • 这是您构建代码时的编译器错误,还是您在 IDE 中遇到错误?
  • @cigien 没有编译器错误,只有 IDE 错误。
  • 我在你的屏幕截图中没有看到任何“语法错误”,只有一些 Intellisense shortcomings。让您的编译器拥有最终决定权,而不是 Unintellinonsense。 :)
【解决方案2】:

您提供的代码compiles。但是,它存在一些问题。

如果输入映射类型使用了与默认不同的模板参数,它将无法编译。这是因为keys签名除了前两个之外没有推导出Table的模板参数。例如,像这样调用keys 会失败:

std::map<int, std::string, std::greater<int>> stringTable;
auto stringTableKeys = keys(stringTable); // the deduced type of table argument
                                          // does not match that of stringTable

要解决此问题,您可以按如下方式调整 keys 签名:

template <typename Key, typename... Args,
    template <typename, typename...> typename Table>
std::vector<Key> keys(const Table<Key, Args...>& table)
{
    std::vector<Key> keys;
    keys.reserve(table.size());
    for (const auto& [key, value] : table)
        keys.push_back(key);
    return keys;
}

这是code

但是,如果参数的类型与预期的template &lt;typename, typename...&gt; typename Table 签名不匹配,这仍然会中断,其中第一个模板参数表示密钥类型。为了缓解这种情况,您可以使用由所有关联容器定义的嵌套 key_typevalue_type 类型。例如:

template <typename Container>
std::vector<typename Container::key_type> keys(const Container& table)
{
    std::vector<typename Container::key_type> keys;
    keys.reserve(table.size());
    for (const auto& [key, value] : table)
        keys.push_back(key);
    return keys;
}

或:

template <typename Container>
std::vector<std::remove_const_t<
    typename Container::value_type::first_type>>
    keys(const Container& table)
{
    std::vector<std::remove_const_t<
        typename Container::value_type::first_type>> keys;
    keys.reserve(table.size());
    for (const auto& [key, value] : table)
        keys.push_back(key);
    return keys;
}

请注意,所有关联容器的Container::value_typestd::pair&lt;const Key, Value&gt;,并且std::pair 定义了first_typesecond_type 嵌套类型定义以访问其元素类型。我们需要使用std::remove_const_t来去掉key类型的const限定。

keys 的后一个版本特别有用,因为它不仅适用于关联容器,而且适用于任何std::pairs 序列。见代码here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多