删除默认构造函数会导致STL容器中的默认分配器出现问题

时间:2015-03-30 12:40:39

标签: c++ oop constructor stl allocator

我有一个看起来像这样的课程:

class PasswordCategory
{
public:
    PasswordCategory(const std::string&);
    ~PasswordCategory();

    PasswordCategory() = delete;
    ...
}

这会导致与分配器相关的编译器错误:

  

错误C2280:' PasswordCategory :: PasswordCategory(void)' :尝试   引用已删除的函数
文件:xmemory0:577

IDE是VS 2013社区。

我假设这是因为其他地方我正在使用这些类别的矢量。

std::vector<PasswordCategory> m_categories;

我只使用emplace_back(string)将元素插入其中,但是,似乎PasswordCategory的默认分配器正在尝试使用PasswordCategory的默认构造函数,但由于删除了它,因此会抛出错误。

如果我提供默认构造函数,一切正常,但我想知道如果没有默认构造函数我可以如何缓解这个问题,如果有的话?

我想到了以下解决方案:

  1. 为我的类提供构建我的类的自定义分配器。但是,这并没有解决我希望我的字符串参数是非可选的问题。
  2. 提供默认构造函数,只使用一些参数调用我的其他构造函数。这也没有解决这个论点是非选择性的问题,并且不应该是默认的。
  3. 使用引用或指针的向量而不是值向量。这似乎是最合理的解决方案,但它引入了手动管理内存的需要,除非我们使用unique_ptr或类似的东西。
  4. 我想知道我是否可以某种方式禁止无法构建我的班级,同时仍然可以按标准容器中的值使用它?

    感谢任何答案和见解,提前谢谢。

    P.S。这是我正在做的一个小项目,以便更好地理解C ++,我试图避免大多数常见的陷阱,并尽可能使一切尽可能可靠,这样当我打算开展更大的项目时,我会更容易避免这些常见的陷阱。我试着以不同的方式提出问题,但没有找到我的问题的答案,所以相反,我问自己的问题。

2 个答案:

答案 0 :(得分:3)

std::vector本身并不要求T成为DefaultConstructible类型:

直到C ++ 11:

  

T必须符合CopyAssignable和CopyConstructible的要求。

自C ++ 11以来:

  

对元素施加的要求取决于对容器执行的实际操作。通常,要求元素类型是完整类型并满足Erasable的要求,但许多成员函数强加了更严格的要求。

有关详细信息,请参阅this page

但是,您可以对容器执行操作,这涉及创建隐式实例以及导致此错误的原因。如果你可以跟踪它们并消除它们,那么一切都应该可以正常工作,因为如果不使用默认构造函数就不需要它。

考虑您的建议:

1。为我的类提供构造我的类的自定义分配器。

这不会有帮助 - std::allocator不对默认构造元素负责,因为它根本没有定义这样的功能。请参阅std::allocator::construct

修改 这里有点小错误,我没有注意到C ++ 11中的小变化:

直到C ++ 11

void construct( pointer p, const_reference val );

自C ++ 11以来

template< class U, class... Args >
void construct( U* p, Args&&... args );

2. 提供默认构造函数,只使用一些参数调用我的其他构造函数。

这也没有解决这个论点是非选择性的问题,并且不应该被默认。 这也不是完全便携的。一些编译器(如VC11)不支持委托构造函数。

3. 使用引用或指针的向量而不是值向量。

这似乎是最合理的解决方案,但它引入了手动管理内存的需要,除非我们使用unique_ptr或类似的东西。 不太有效 - 您无法创建引用容器。最接近的解决方案是容器,它包含std::reference_wrapper个。原始/智能指针的容器也是一种选择,但这是重点,事情开始变得混乱。

此外,在您的原始代码中,没有必要声明已删除的默认构造函数 - 如果您声明任何构造函数,则表示没有默认构造函数(除非您定义它)​​并且编译器赢了& #39; t生成任何。

答案 1 :(得分:1)

原则上,你不能:标准容器要求包含的对象是可默认构造的。 (见Mike Seymour的评论)。

原则上你应该能够,除非你在内部使用需要默认构造的操作。

也就是说,你可以简单地创建一个空构造函数(默认成员为sane /调用带有一些参数的另一个构造函数)。如果正确编写客户端代码,则不会遇到使用默认值初始化的对象。

需要默认构造对象的操作通常是调整大小,还有一些需要创建内部对象的操作(即除非您想要保留元素并使用它而不显式初始化它,否则您应该没有问题)。 / p>