【问题标题】:Issue with typedefining iterators with in the class declaration [closed]在类声明中定义迭代器的问题[关闭]
【发布时间】:2016-10-25 22:27:10
【问题描述】:

我正在编写一个写得很糟糕的遗留代码,只能在 Microsoft Visual Studios 和 Visual C++ 编译器中编译。由于构建时间错误,GCC、G++ 或 Clang 都无法编译代码。我已将问题缩小到以下类声明,它在类声明中实例化 class typeSTL 容器:

#include <map>
#include <set>
#include <iomanip>
#include <string>
#include <cmath>
#include <iterator>

#include <unordered_map>
#include <bits/unique_ptr.h>

#define HASH_MAP unordered_map

using namespace std;

namespace XYZ {
       class abc {
           public:
              typedef HASH_MAP<double, abc> MAP; // This is the problem ?
              typedef MAP::iterator Iterator;
              typedef MAP::const_iterator ConstIterator;
              typedef pair<double, abc> Pair;

              bool less(abc::Pair& a, abc::Pair& b) { return a.first < b.first; }

           public:
              abc() {}
              ~abc() { }

       };
    }

我想知道在保留代码结构的同时重构此代码段的最佳方法是什么。例如,我试图使用指针类型(即typedef HASH_MAP&lt;double, XYZ*&gt; MAP)创建MAP 定义,但是此更改适用于 GCC 编译器,因为我要更改为指针类型,我必须深入研究代码库并修改大部分代码库,因为该类在其他依赖代码中起着关键作用。

所以我想知道是否有替代方法可以解决此问题,而无需对原始代码库进行重大更改。我正在考虑制作类似的friend 类。

以下是编译错误:

    In file included from /usr/include/c++/4.8/bits/stl_algobase.h:64:0,
                 from /usr/include/c++/4.8/bits/stl_tree.h:61,
                 from /usr/include/c++/4.8/map:60,
                 from /home/user/work/wxy.h:4,
                 from /home/user/work/abc.h:4,
                 from /home/user/work/abc.cpp:1:
/usr/include/c++/4.8/bits/stl_pair.h: In instantiation of ‘struct std::pair<const double, XYZ::abc>’:
/usr/include/c++/4.8/type_traits:615:28:   required from ‘struct std::__is_destructible_impl<std::pair<const double, XYZ::abc> >’
/usr/include/c++/4.8/type_traits:637:12:   required from ‘struct std::__is_destructible_safe<std::pair<const double, XYZ::abc>, false, false>’
/usr/include/c++/4.8/type_traits:652:12:   required from ‘struct std::is_destructible<std::pair<const double, XYZ::abc> >’
/usr/include/c++/4.8/type_traits:116:12:   required from ‘struct std::__and_<std::is_destructible<std::pair<const double, XYZ::abc> >, std::__is_direct_constructible_impl<std::pair<const double, XYZ::abc>, const std::pair<const double, XYZ::abc>&> >’
/usr/include/c++/4.8/type_traits:817:12:   required from ‘struct std::__is_direct_constructible_new_safe<std::pair<const double, XYZ::abc>, const std::pair<const double, XYZ::abc>&>’
/usr/include/c++/4.8/type_traits:895:12:   [ skipping 4 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/c++/4.8/type_traits:968:12:   required from ‘struct std::__is_copy_constructible_impl<std::pair<const double, XYZ::abc>, false>’
/usr/include/c++/4.8/type_traits:974:12:   required from ‘struct std::is_copy_constructible<std::pair<const double, XYZ::abc> >’
/usr/include/c++/4.8/bits/alloc_traits.h:540:12:   required from ‘struct std::__is_copy_insertable<std::allocator<std::pair<const double, XYZ::abc> > >’
/usr/include/c++/4.8/bits/alloc_traits.h:560:63:   required by substitution of ‘template<class _Alloc> using __check_copy_constructible = std::__allow_copy_cons<std::__is_copy_insertable<_Alloc>::value> [with _Alloc = std::allocator<std::pair<const double, XYZ::abc> >]’
/usr/include/c++/4.8/bits/unordered_map.h:97:11:   required from ‘class std::unordered_map<double, XYZ::abc>’
/home/user/work/abc.h:27:20:   required from here
/usr/include/c++/4.8/bits/stl_pair.h:102:11: error: ‘std::pair<_T1, _T2>::second’ has incomplete type
       _T2 second;                /// @c second is a copy of the second object
           ^
In file included from /home/user/work/abc.cpp:1:0:
/home/user/work/abc.h:24:11: error: forward declaration of ‘class XYZ::abc’
     class abc {
           ^

【问题讨论】:

  • 命名空间怎么可能是std::unordered_map的模板参数?
  • 那只是一个typedef 没有实例化
  • 我很好奇为什么public: 被声明了两次? abcnamespace XYZ 中的任何一个类都缺少左侧 }
  • @user2079303 一定是笔误,其他类方法也是XYZ()~XYZ()
  • #include &lt;utility&gt;了吗?你在用using namespasce std;吗?如果不是,那么标准库中的所有名称都需要在名称前加上std::

标签: c++ c++11 stl iterator incomplete-type


【解决方案1】:

问题在于(无论出于何种原因——见鬼,它只是一个 typedef!)与迭代器有关。如果将它们从类代码中移出,以便类定义在它们的定义点完成,它会编译(使用 g++)。也许应该将Iterator 重命名为MAP_Iterator 或类似的名称。我可以想象客户端代码中所需的更改是可管理的

编辑:在您发表评论后,我想到将迭代器 typedef 放在一个名为 abc(原始类名)的类中,以保持与客户端代码的源兼容性。实际的类定义被移动到客户端代码不需要显式使用的基类。该映射包含基类的对象,这涉及在存储或检索真实abcs 时进行切片和反向转换。可能无法在映射中保存 abc 对值的引用,但下面基于值的简单示例(使用迭代器)有效。源码中有几个cmets。

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

// The "original" abc
class abcBase 
{
       public:

        typedef unordered_map<double, abcBase> MAP; // This is the problem ?
          typedef pair<double, abcBase> Pair;
          bool less(abcBase::Pair& a, abcBase::Pair& b) { return a.first < b.first; }
          string tag;
       public:
          abcBase(string t): tag(t){}
          abcBase() = default;
          ~abcBase() { }
};

// The abc presented to the users for source compatibility.
// There is a conversion
// from base to derived via constructor. 
//
// Note that a MAP
// holds abcBase objects, not abc objects! We need to convert them
// when we store and when we read.
// Conversion derived -> base is via slicing
// (which does not do any harm as long as we do not define
// data members in derived).
class abc: public abcBase
{
    public:
    // conversion constructor
    abc(const abcBase &b): abcBase(b){}
    abc(string t): abcBase(t){}
    abc() = default;
    typedef MAP::iterator Iterator;
    typedef MAP::const_iterator ConstIterator;
};

int main()
{
    abc::MAP m;
    abc a("a"),b("b"),c("c");
    m[1.0] = a; // conversion abc -> abcBase ...
    m[2.0] = b;
    m[3.0] = c;

    a = m[1.0]; // conversion abcBase -> abc via ctor

    for( abc::Iterator i = m.begin(); i != m.end(); ++i)
    {
        cout << "key: " << i->first << ", val: " << i->second.tag << endl;
    }
    return 0;
}       

【讨论】:

  • 感谢您的解决方案。它确实编译了这段代码,但在客户端代码中它失败了,因为iterator 不是这个类的一部分。因此,每当声明一个类实例并调用它的迭代器时,它都会抱怨。有什么建议让iterators 成为课堂的一部分吗?
  • 感谢您的帮助!您的解决方案解决了这个问题。为客户端代码使用命名空间标识符。
  • 啊,很好!我刚刚发布了一个带有派生类构造的更新解决方案,但它有点像黑客......
【解决方案2】:

您使用的是std::unordered_map,所以您使用的是 C++11。

所以我认为使用std::unique_ptrtypedef HASH_MAP&lt;double, std::unique_ptr&lt;XYZ&gt;&gt; MAP而不是typedef HASH_MAP&lt;double, XYZ*&gt; MAP)可以帮助您减少以下修改。

例如编译如下代码

#include <utility>
#include <memory>
#include <unordered_map>

using namespace std;

#define HASH_MAP unordered_map 

namespace abc {
   class XYZ {
      private:
         int  _test;

      public:
         typedef HASH_MAP<double, std::unique_ptr<XYZ>> MAP; // This is the problem ?
         typedef MAP::iterator Iterator;
         typedef MAP::const_iterator ConstIterator;
         typedef pair<double, std::unique_ptr<XYZ>> Pair;

         bool less(XYZ::Pair& a, XYZ::Pair& b) { return a.first < b.first; }

      public:
         XYZ():_test(0) {}
         ~XYZ() { }

         // Some function definitions
   };
};


int main ()
 {
   return 0;
 }

显然我假设你的命名空间是abc,你的类名是XYZ;您不能在地图中放置命名空间。

ps:对不起,我的英语不好。

【讨论】:

  • 使用unique_ptr而不是原始指针会更好,但它会减少客户端代码中所需的更改量吗?
  • @PeterA.Schneider - 不是很多,但可以避免 delete 部分
  • @max66 感谢您的解决方案。我之前采用了指针路线..不是unique_ptr,而是原始指针。似乎需要对客户端代码库进行重大更改。
  • @kishansudu - 不客气;如果您的地图/对不在 XYZ 类中(如果 XYZ 是类),您可以将 typedefs 放在类之外(参见 Peter A. Schneider 的答案);如果您必须将地图/对作为XYZ 的成员,我不知道如何避免重大更改
  • @max66 谢谢!不,他们不是班级的成员。只是 typedef,所以我会尝试 Peter A. Schneider 的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多