为啥 C++ 映射类型参数在使用 [] 时需要一个空的构造函数?

Posted

技术标签:

【中文标题】为啥 C++ 映射类型参数在使用 [] 时需要一个空的构造函数?【英文标题】:Why does the C++ map type argument require an empty constructor when using []?为什么 C++ 映射类型参数在使用 [] 时需要一个空的构造函数? 【发布时间】:2009-03-29 23:49:59 【问题描述】:

另请参阅 C++ standard list and default-constructible types

不是主要问题,只是烦人,因为我不希望我的类在没有特定参数的情况下被实例化。

#include <map>

struct MyClass

    MyClass(int t);
;

int main() 
    std::map<int, MyClass> myMap;
    myMap[14] = MyClass(42);

这给了我以下 g++ 错误:

/usr/include/c++/4.3/bits/stl_map.h:419: 错误:没有匹配函数调用‘MyClass()’

如果我添加一个默认构造函数,这编译得很好;我确定这不是由不正确的语法引起的。

【问题讨论】:

上面的代码在 MinGW (g++ 3.4.5) 和 MSVC++2008 上编译得很好,前提是给出了 MyType 的 typedef 并且在类的末尾附加了一个分号。您必须在做其他事情(例如,调用 bb 提到的 operator[])——请发布 full 代码。 啊,是的,你是对的。会的。 是的,不使用 myMap 你不知道需要为地图类编译什么。哪个 stl 库提供程序和版本也可能有帮助。 【参考方案1】:

此问题与 operator[] 相关。引用自 SGI 文档:

data_type&amp; operator[](const key_type&amp; k) - 返回对对象的引用 与特定的 钥匙。如果地图还没有 包含这样一个对象,operator[] 插入默认对象 data_type().

如果您没有默认构造函数,您可以使用插入/查找函数。 以下示例工作正常:

myMap.insert( std::map< int, MyClass >::value_type ( 1, MyClass(1) ) );
myMap.find( 1 )->second;

【讨论】:

优秀的答案——还要注意 C++11 中的 emplace 作为 insert 的简洁替代品。 为什么std::&lt;map&gt;::value_type 出现在insert 调用中? 为什么默认构造函数需要用户自定义? @schuess 我看不出有什么原因:= default 应该可以正常工作。 条件“地图尚未包含这样的对象”将在运行时进行评估。为什么会出现编译时错误?【参考方案2】:

是的。 STL 容器中的值需要维护复制语义。 IOW,它们需要表现得像原始类型(例如 int),这意味着它们应该是默认可构造的。

如果没有这个(和其他要求),就很难在实现 STL 容器的数据结构上实现各种内部复制/移动/交换/比较操作。

在参考 C++ 标准时,我发现我的回答并不准确。 默认构造实际上不是必需的

从 20.1.4.1 开始:

默认构造函数不是 必需的。某些容器类 成员函数签名指定 默认构造函数作为默认值 争论。 T() 必须是明确定义的 表达...

因此,严格来说,如果您碰巧使用了在其签名中使用默认构造函数的容器函数,则您的值类型只需要是默认可构造的。

存储在 STL 容器中的所有值的实际要求 (23.1.3) 是 CopyConstructibleAssignable

对于特定容器还有其他特定要求,例如 Comparable(例如,对于地图中的键)。


顺便说一句,以下在comeau 上编译时没有错误:

#include <map>

class MyClass

public:
    MyClass(int t);
;

int main()

    std::map<int, MyClass> myMap;

所以这可能是一个 g++ 问题。

【讨论】:

你认为 bb 会关注 [] 运算符吗? 该代码可能会编译,因为您没有调用 myMap[]【参考方案3】:

检查 stl::map 存储类型的要求。许多 stl 集合要求存储类型包含一些特定的属性(默认构造函数、复制构造函数等)。

stl::map 需要不带参数的构造函数,因为它在使用键调用 operator[] 时使用,而该键尚未被映射保留。在这种情况下,operator[] 插入由使用无参数构造函数构造的新键和值组成的新条目。然后返回这个新值。

【讨论】:

【参考方案4】:

假设你有以下情况

class Person

public:
    Person(int age) :age(age)
    Person()  // default ctor
    int age;
;

map<int, Person> m;

// accessing not-existent key, results in assigning default value to that key
m[10];

// creates default object for key:20 first then assigns age
m[20].age = 32;

如果您想为不存在的密钥分配age,应该怎么办?

对于具有null 类型的语言,例如javascript,映射返回null,并由用户在访问对象或其内部字段之前进行检查。

c++ 采用了不同的方法,并使用默认构造函数创建了Person,因此一起避免了 null

【讨论】:

【参考方案5】:

检查是否:

您忘记了“;”在类声明之后。 MyType 应该被相应地声明。 那里没有默认构造函数...

我认为 std::map 声明似乎是正确的。

【讨论】:

如果我添加一个默认构造函数,编译正常。【参考方案6】:

很可能是因为 std::pair 需要它。 std::pair 使用值语义保存两个值,因此您需要能够在没有参数的情况下实例化它们。所以代码在不同的地方使用 std::pair 将映射值返回给调用者,这通常是通过实例化一个空对并在返回本地对之前将值分配给它来完成的。

您可以通过使用 map > 的智能指针来解决这个问题,但这会增加检查空指针的开销。

【讨论】:

+0。 pair 可以很好地用于缺少默认构造函数的类型 T 和 U —— 在这种情况下唯一不能使用的是 pair 自己的默认构造函数。 map 的任何质量良好的实现都不会使用此默认构造函数,因为它限制了 K 和 V 可以是什么。

以上是关于为啥 C++ 映射类型参数在使用 [] 时需要一个空的构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在springboot中mybatis的映射文件不能使用别名

为啥 C++ 构造函数在继承中需要默认参数?

当一个函数无返回值时,函数的类型应定义为啥

为啥g ++仍然需要-latomic

为啥我得到“程序需要'ntext/nchar/nvarchar'类型的参数'@statement'。”当我尝试使用 sp_executesql 时?

C++:调用无参数的构造函数为啥不加括号