【问题标题】:Detect if type is a "mapping"检测类型是否为“映射”
【发布时间】:2017-07-11 18:37:04
【问题描述】:

我想使用它们的 ::iterator 成员类型将 c++ 容器解析为另一个对象。迭代器成员类型指向单一类型对象(向量、队列等)的容器将变成类列表对象,而迭代器成员类型指向std::pair的容器将变成类地图对象.

我正在尝试编写一个成员函数来检测后一种容器,但它不起作用。到目前为止,这是我所拥有的:

#include <tuple>
#include <iterator>
#include <type_traits>

template <typename T>
struct is_pair : std::false_type { };

template <typename T, typename U>
struct is_pair<std::pair<T, U>> : std::true_type { };

template <typename T>
constexpr bool is_pair_v = is_pair<T>::value;

template <typename...>
struct is_mapping : std::false_type { };

template <typename Container>
struct is_mapping<Container, std::enable_if_t<
    is_pair_v<std::iterator_traits<typename Container::iterator>::value_type>
>> : std::true_type { };

template <typename T>
constexpr bool is_mapping_v = is_mapping<T>::value;

#include <map>
#include <vector>
#include <iostream>

int main() {
    std::cout << "is_pair:" << std::endl;
    std::cout << "Map:    " << is_pair_v<std::iterator_traits<std::map<int, int>::iterator>::value_type> << std::endl;
    std::cout << "Vector: " << is_pair_v<std::iterator_traits<std::vector<int>::iterator>::value_type>   << std::endl;
    std::cout << std::endl;
    std::cout << "is_mapping:" << std::endl;
    std::cout << "Map:    " << is_mapping_v<std::map<int, int>> << std::endl;
    std::cout << "Vector: " << is_mapping_v<std::vector<int>>   << std::endl;
}

由于某种原因,is_mapping_v 始终为 false,代码导致以下输出:

$ g++ -std=c++14 temp.cc && ./a.out
is_pair:
Map:    1
Vector: 0

is_mapping:    
Map:    0
Vector: 0

我的代码有什么问题?


注意:这不是Checking if a type is a map 的副本。该问题的答案使用::key_type::mapped_type 成员来检测地图(对于std::multimap 等类,它会失败)。我还在代码后面明确使用了迭代器指向std::pairs 的事实,因此检查它更有意义。

【问题讨论】:

标签: c++ template-meta-programming


【解决方案1】:
is_pair_v<std::iterator_traits<typename Container::iterator>::value_type>

应该是

is_pair_v<typename std::iterator_traits<typename Container::iterator>::value_type>

因为value_type 是一种类型。如果没有typename,它将被解析为一个值,而enable_if 则失败,从而回退到主模板。

您在main 中的测试产生正确值的原因是因为那里的模板已经被实例化并且value_type 是类型还是值没有歧义。

第二个错误是你的主模板

template<typename...>

应该是

template<typename, typename = void>

否则,is_mapping&lt;T&gt; 将永远不会是具有两个参数的特化,因为参数计数不匹配。

Live

【讨论】:

【解决方案2】:

编译器没有选择部分特化的结构。这有两个原因:

  1. 您在is_pair_v&lt;std::iterator_traits&lt;typename Container::iterator&gt;::value_type&gt; 之外执行enable_if。编译器会将std::iterator_traits&lt;...&gt;::value_type 视为一个值而不是一个类型,但 SFINAE 会启动,它会被默默地删除。
  2. 使用template &lt;typename...&gt; 可以避免选择专业化。如果我是对的,这是因为is_mapping&lt;T&gt; 可以使用初始定义解析为is_mapping&lt;T&gt;,或者使用专业化解析为is_mapping&lt;T, void&gt;。在这种情况下,is_mapping&lt;T&gt; 似乎是首选。

    解决这个问题就像将其更改为 template &lt;typename, typename = void&gt; 一样简单:

template <typename, typename = void>
struct is_mapping : std::false_type { };

template <typename Container>
struct is_mapping<Container, std::enable_if_t<
    is_pair_v<typename std::iterator_traits<typename Container::iterator>::value_type>
>> : std::true_type { };

不过,is_mapping 的更简单实现是:

template <typename Container>
struct is_mapping : is_pair<
    typename std::iterator_traits<typename Container::iterator>::value_type
> {};

基本上,您的版本看起来像

if (is_pair(...)) return true;
else return false;

而你可以简单地做

return is_pair(...)

【讨论】:

  • 更简单的实现不适用于没有::iterator 成员类型的类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-27
  • 1970-01-01
  • 1970-01-01
  • 2019-10-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多