微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C ++映射,使用const引用作为值类型,这是什么问题? 代码示例1 代码示例2 代码示例3

如何解决C ++映射,使用const引用作为值类型,这是什么问题? 代码示例1 代码示例2 代码示例3

今天,我看到了老板的代码,该代码使用const引用作为map的值类型。

这是代码

class ConfigManager{
    public:
        map<PB::Point,const PB::WorldPoint&,compare_point> world_point;
    //the rest are omitted
};

({PB是Google Protobuf,我们使用的是Protobuf库。我对此了解不多,或者与问题是否相关。)

该类的作用是读取一些配置文件,并将其放入map文件中进行搜索


起初我很惊讶,因为我还没有看到map的引用作为其值,例如map<int,classA&> aMap

然后我搜索了SO,这两个问题告诉我我不能这样做。

C++: Is it possible to use a reference as the value in a map?

STL map containing references does not compile

然后我尝试了这段代码,确实无法编译:

代码示例1

struct A {
    int x = 3;
    int y = 4;
};

map<int,A&> myMap;

int main() {
    A a;
    myMap.insert(make_pair(1,a));
}

但是如果我将map<int,A&> myMap;更改为map<int,const A&> myMap;,它将编译。

但是发生了另一个问题。使用map<int,const A&> myMap;,我无法使用[]来获得货币对,但是我可以使用map.find()

(老板告诉我使用map.find()后不能使用[]

代码示例2

struct A {
    int x = 3;
    int y = 4;
};

map<int,const A&> myMap;

int main() {
    A a;
    myMap.insert(make_pair(1,a));

    //can't compile
    cout << myMap[1].x << " " << myMap[1].y << endl; 
    
    //can work
    //auto it = myMap.find(1);
    //cout << it->second.x << " " << it->second.y << endl;
}

所以直到现在,我一直认为我的老板是正确的。他的代码是正确的。


最后一个故事是我向一些在线朋友展示了该代码。他们注意到了一个问题。

代码示例3

#include <map>
#include <iostream>
#include <string>
using namespace std;

struct A {
    int x = 3;
    int y = 4;
    ~A(){
        cout << "~A():" << x << endl;
        x = 0;
        y = 0;
    }
};

map<string,const A&> myMap;

int main() {
    A a;
    cout << a.x << " " << a.y << endl;
    myMap.insert(make_pair("love",a));
    a.x = 999;
    cout << "hello" << endl;
    auto s = myMap.find("love");
    cout << s->second.x << " " << s->second.y << endl;
}

输出为:

3 4
~A():3
hello
0 0
~A():999

如果我正确理解了输出(如果我弄错了请纠正我),则表明:

  1. make_pair("love",a)创建一个对象pair<"love",temproray copy of a>。一对将inserted放入myMap
  2. 以某种方式,我不知道它是如何发生的,temporary copy of a立即被破坏。对我来说,这意味着temporary copy of a的内存现在不归任何人所有,如果我理解正确的话,它现在是可以用任何值填充的内存的可用空间。

所以现在我又变得困惑了。

我的问题是:

  1. 代码示例3 会怎样?我的理解正确吗?为什么在声明后temporary copy of a被破坏?不使用const引用可以延长临时对象的寿命吗?我的意思是,我认为在main完成之前不应该破坏它。

  2. 我老板的代码不正确并且非常危险吗?

解决方法

为什么在语句后立即破坏了临时副本?

因为(在大多数情况下)临时人员是这样工作的。有效期至创建它们的语句的末尾。在这种情况下,暂时寿命的扩展名不适用,请参见here。 TLDR版本是

通常,不能通过以下方式进一步延长临时期限 “传递”:第二个引用,从引用初始化为 绑定的临时对象,不会影响其寿命。

我可以使用const引用作为地图的值类型吗?

是的,只要您意识到向映射添加const引用对所引用对象的生存期没有影响。您的老板代码也不正确,因为make_pair返回的临时文件在语句末尾被销毁了。

,

您可以改用std:: unique_ptr<A>。然后用emplace代替insert

using value_t=std:: unique_ptr<A>;
std::map<int,value_t> myMap;
myMap.emplace(1,new A);
myMap[1]=new A{5,6};
myMap[1]->x=7;

有关std:: unique_ptr<A>的更多信息: https://en.cppreference.com/w/cpp/memory/unique_ptr

,

代码示例3会怎样?我的理解正确吗?

您的解释已经结束。 std::pair返回的std::make_pair是临时对象。临时std::pair包含a的副本。在表达式的末尾,该对被破坏,这也破坏了其元素,包括a的副本。

为什么在语句后立即破坏了临时副本?不使用const引用可以延长临时对象的寿命吗?我的意思是,我认为在主体完成之前不应破坏它。

这里的临时变量是std::make_pair的结果,该结果被用作成员函数insert的参数。这里适用的相关规则是:

无论何时将引用绑定到临时对象或其子对象,临时对象的生存期都会延长以匹配引用的生存期,但以下情况除外:

  • [...]
  • 在函数调用中存在与参考参数的临时绑定,直到包含该函数调用的完整表达式的结尾[...]
  • [...]

source

包含函数调用的完整表达式为表达式myMap.insert(make_pair(1,a));,这意味着std::make_pair的结果的生存期在函数返回后(包括其中包含的A)结束。新的std::map元素将引用临时A中的std::pair,一旦insert返回,该元素就会悬空。

我老板的代码不正确并且非常危险吗?

是的,myMap包含悬空的引用。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。