用类修剪C ++类型

时间:2018-07-04 15:37:43

标签: c++ type-punning

我正在编写一些包装std :: unordered_map类型的C ++代码,在这里我想隐藏基础类型并将其显示为另一种类型。更具体地说,我想用另一种类型包装std :: unordered_map中的std :: pair。为了争辩,让我们假设包装器看起来像这样……

template <typename ActualT >
class wrapper final
{
private:
    ActualT actual_;
public:
    //Some constructors...
    typename ActualT::first_type & get_first()
    {
        return actual_.first;
    }
    typename ActualT::second_type & get_second()
    {
        return actual_.second;
    }
};

我的理由是,由于wrapper类只有一个成员,而该成员正是它要包装的确切类型,因此将引用从原始类型转换为包装器类型应该没问题,但是struct的类型兼容性指出成员应当具有相同的类型和名称,以使这些类型兼容。以这种方式使用类型校正可能会导致不确定的行为或对齐问题吗?

using my_map = std::unordered_map < int, int >;
my_map m;
//Do some inserts...
reinterpret_cast<wrapper<typename my_map::value_type>&>(*m.find(10)).get_second() = 1.0;

我希望允许客户端代码访问映射的条目,而无需知道映射返回的对。我还想编写一个自定义的正向迭代器,因此需要返回对该条目的引用。将对对的引用转换为对充当包装器的类的引用会被视为危险吗?

也许有更好的方法来实现这一目标?

2 个答案:

答案 0 :(得分:2)

所以您可以清理它,但我不建议这样做:

#include <unordered_map>
#include <iostream>
using namespace std;

template <class Key, class Value>
class wrapper
{
public:
   explicit wrapper(std::pair<const Key, Value>& kvp)
      : _key{kvp.first}
      , _value{kvp.second}
   {}

   const Key& key() const { return _key; }
   Value& value()         { return _value; }

private:
   const Key& _key;
   Value& _value;
};

int main()
{
   unordered_map<int,int> m;

   m[1] = 1;
   m[3] = 3;

   auto it = m.find(1);

   wrapper w{*it};
   w.value() = 30;

   std::cout << w.key() << " -> " << w.value() << '\n';
}

以上内容有效地向班级用户隐藏了pair。它不处理异常(例如,find()返回end()),并且不保证生存期。它比您现有的要好一些,因为它不需要reinterpret_cast到不相关的类型。

但是,mapunordered_mapset等将返回的迭代器成对存储只是库的一部分-这是规范的形式,我看不出它的好处保护人们免受它侵害。

答案 1 :(得分:2)

这绝对是不确定的行为。

认真考虑您的优先事项。

表格的一些免费功能

const my_map::key_type & MeaningfulNameHere(my_map::reference)

将为您赋予有意义的名称大有帮助。

如果必须用不同的名称包装标准库,则只需使用非显式构造函数并存储引用即可。

template <typename Map>
class entry final
{
private:
    typename Map::reference ref;
public:
    entry(Map::reference ref) : ref(ref) {}

    const typename Map::key_type & key()
    {
        return ref.first;
    }
    typename Map::mapped_type & value()
    {
        return ref.second;
    }
};

如果您真的需要迭代器取消引用到entry,则可以。但是您只需从entry返回的Map::reference中隐式实例化Map::iterator::operator*,就不需要自定义迭代器。

template <typename Map>
class entry_iterator
{
private:
    typename Map::iterator it;
    entry<Map> entry;
public:
    entry<Map>& operator*() { return entry; }
    entry_iterator operator++() { ++it; entry = *it; return *this; }
    // etc
}