删除默认构造函数会导致 STL 容器中的默认分配器出现问题
Deleting default constructor causes problems with default allocator in STL containers
我有一个 class 看起来像这样:
class PasswordCategory
{
public:
PasswordCategory(const std::string&);
~PasswordCategory();
PasswordCategory() = delete;
...
}
这会导致与分配器相关的编译器错误:
error C2280: 'PasswordCategory::PasswordCategory(void)' : attempting
to reference a deleted function
File: xmemory0:577
IDE 是 VS 2013 社区。
我假设发生这种情况是因为我在其他地方使用了这些类别的矢量。
std::vector<PasswordCategory> m_categories;
我只使用 emplace_back(string) 将元素插入其中,但是,似乎 PasswordCategory 的默认分配器正在尝试使用 PasswordCategory 的默认构造函数,但由于它已被删除,它会抛出一个错误。
如果我提供默认构造函数,一切正常,但我想知道如何在没有默认构造函数的情况下缓解这个问题?
我想到了以下解决方案:
- 为我的 class 提供自定义分配器来构造我的 class。但是,这并没有解决我希望我的字符串参数是非可选的问题。
- 提供默认构造函数,该构造函数仅使用一些参数调用我的其他构造函数。这也没有解决这个参数是非可选的并且不应该被默认的问题。
- 使用引用或指针向量而不是值向量。这似乎是最合理的解决方案,但是它需要手动管理我们的内存,除非我们使用 unique_ptr 或类似的东西。
我想知道我是否可以以某种方式禁止 class 的无参数构造,同时仍然能够按值在标准容器中使用它?
感谢任何答案和见解,提前致谢。
P.S。这是我为了更好地理解 C++ 而正在做的一个小项目,我试图在其中避免大多数常见的陷阱并使一切尽可能可靠,这样当我要从事更大的项目时,我会更容易避免这些常见的陷阱。我尝试以不同的方式提出问题,但没有找到我的问题的答案,所以我问自己的问题。
原则上,您不能:标准容器要求包含的对象是默认可构造的。 (请参阅 Mike Seymour 的评论)。
原则上你应该可以,除非你使用内部需要默认构造的操作。
就是说,您可以简单地创建一个空的构造函数(将成员默认为 sane/calls 另一个带有一些参数的构造函数)。如果您正确编写客户端代码,您将不会遇到使用默认值初始化的对象。
需要默认构造对象的操作通常是调整大小,还有一些其他需要创建内部对象的操作(即,除非您想保留一个元素并在不显式初始化它的情况下使用它,否则您应该没有问题) .
std::vector
本身并不要求 T
是 DefaultConstructible
类型:
直到 C++ 11:
T must meet the requirements of CopyAssignable and CopyConstructible.
自 C++ 11 起:
The requirements that are imposed on the elements depend on the actual operations performed on the container. Generally, it is required that element type is a complete type and meets the requirements of Erasable, but many member functions impose stricter requirements.
详情见this page。
但是,您可能会在容器上执行涉及创建隐式实例的操作,这就是您收到此错误的原因。如果您可以跟踪并消除它们,一切都应该正常,因为如果不使用默认构造函数,则根本不需要它。
正在考虑您的提议:
1. 为构建我的 class 的 class 提供自定义分配器。
这无济于事 - 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_wrappers 的容器。 raw/smart 指针的容器也是一个选项,但这就是事情开始变得混乱的地方。
此外,在您的原始代码中,无需声明已删除的默认构造函数 - 如果您声明 any 构造函数,则意味着没有默认构造函数(除非您定义它)和编译器不会生成任何。
我有一个 class 看起来像这样:
class PasswordCategory
{
public:
PasswordCategory(const std::string&);
~PasswordCategory();
PasswordCategory() = delete;
...
}
这会导致与分配器相关的编译器错误:
error C2280: 'PasswordCategory::PasswordCategory(void)' : attempting to reference a deleted function
File: xmemory0:577
IDE 是 VS 2013 社区。
我假设发生这种情况是因为我在其他地方使用了这些类别的矢量。
std::vector<PasswordCategory> m_categories;
我只使用 emplace_back(string) 将元素插入其中,但是,似乎 PasswordCategory 的默认分配器正在尝试使用 PasswordCategory 的默认构造函数,但由于它已被删除,它会抛出一个错误。
如果我提供默认构造函数,一切正常,但我想知道如何在没有默认构造函数的情况下缓解这个问题?
我想到了以下解决方案:
- 为我的 class 提供自定义分配器来构造我的 class。但是,这并没有解决我希望我的字符串参数是非可选的问题。
- 提供默认构造函数,该构造函数仅使用一些参数调用我的其他构造函数。这也没有解决这个参数是非可选的并且不应该被默认的问题。
- 使用引用或指针向量而不是值向量。这似乎是最合理的解决方案,但是它需要手动管理我们的内存,除非我们使用 unique_ptr 或类似的东西。
我想知道我是否可以以某种方式禁止 class 的无参数构造,同时仍然能够按值在标准容器中使用它?
感谢任何答案和见解,提前致谢。
P.S。这是我为了更好地理解 C++ 而正在做的一个小项目,我试图在其中避免大多数常见的陷阱并使一切尽可能可靠,这样当我要从事更大的项目时,我会更容易避免这些常见的陷阱。我尝试以不同的方式提出问题,但没有找到我的问题的答案,所以我问自己的问题。
原则上,您不能:标准容器要求包含的对象是默认可构造的。 (请参阅 Mike Seymour 的评论)。
原则上你应该可以,除非你使用内部需要默认构造的操作。
就是说,您可以简单地创建一个空的构造函数(将成员默认为 sane/calls 另一个带有一些参数的构造函数)。如果您正确编写客户端代码,您将不会遇到使用默认值初始化的对象。
需要默认构造对象的操作通常是调整大小,还有一些其他需要创建内部对象的操作(即,除非您想保留一个元素并在不显式初始化它的情况下使用它,否则您应该没有问题) .
std::vector
本身并不要求 T
是 DefaultConstructible
类型:
直到 C++ 11:
T must meet the requirements of CopyAssignable and CopyConstructible.
自 C++ 11 起:
The requirements that are imposed on the elements depend on the actual operations performed on the container. Generally, it is required that element type is a complete type and meets the requirements of Erasable, but many member functions impose stricter requirements.
详情见this page。
但是,您可能会在容器上执行涉及创建隐式实例的操作,这就是您收到此错误的原因。如果您可以跟踪并消除它们,一切都应该正常,因为如果不使用默认构造函数,则根本不需要它。
正在考虑您的提议:
1. 为构建我的 class 的 class 提供自定义分配器。
这无济于事 - 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_wrappers 的容器。 raw/smart 指针的容器也是一个选项,但这就是事情开始变得混乱的地方。
此外,在您的原始代码中,无需声明已删除的默认构造函数 - 如果您声明 any 构造函数,则意味着没有默认构造函数(除非您定义它)和编译器不会生成任何。