【问题标题】:Possible to create std::map as rvalue?可以将 std::map 创建为右值吗?
【发布时间】:2016-07-26 21:39:51
【问题描述】:

我不确定这是否是我的 C++ 语法错误,还是根本无法完成。

我想定义一个将 std::map 作为构造函数参数的类。然后我想通过传递一个“临时”(适合称之为“右值”?)std::map 来创建该类的一个实例。 IE。我不想创建一个左值 std::map 然后将其传递给构造函数。

这可以实现吗?我尝试了以下方法(注释行显示尝试失败)

#include <map>
#include <string>
#include <iostream>

class Test
{
    public:
        Test(std::map<int,char>& rMap)
        {
            std::map<int,char>::iterator iter;

            for (iter = rMap.begin(); iter != rMap.end(); ++iter)
            {
                mMap[iter->first] = mMap[iter->second];
            }
        }
        virtual ~Test(){}

    protected:
        std::map<int, char> mMap;
};

int main()
{
    std::cout << "Hello world!" << std::endl;

    //Test test({1,'a'});                   // Compile error.
    //Test test(std::map<int,char>(1,'a')); // Also compile error.
    //Test test(std::map<int,char>{1,'a'}); // Yet again compile error.

    return 0;
}

这是我的编译器:

g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11)

编译错误可以根据要求发布,但我不确定如果我的问题是句法,它们是否有用。

谢谢。

【问题讨论】:

  • 你试过Test test(std::map&lt;int, char&gt;{{1, 'a'}});用两组大括号吗?
  • @TimStraubinger - 根据您的建议尝试了此操作,但导致另一个编译错误。
  • 什么“编译错误”?
  • 使用引用 const 的构造函数更新了问题,并且还添加了我创建测试的三次失败尝试中的每一次的编译错误。
  • 请注意,所有编译错误都指向同一行。这些错误与右值无关 - 您正在尝试将 const 迭代器分配给非 const 迭代器。

标签: c++ dictionary std stdmap rvalue


【解决方案1】:

做

Test(std::map<int, char> rMap) : mMap(std::move(rMap)) {}

或

Test(std::map<int, char>&& rMap) : mMap(std::move(rMap)) {}

或

Test(const std::map<int, char>& rMap) : mMap(rMap) {}

临时不能绑定到非 const 左值引用。

并将其用作

Test test({{1,'a'}});
Test test2({{1,'a'}, {2, 'b'}});

【讨论】:

  • 好的。 为什么?没有解释就没有用。不要只是代码转储。
  • @Jarod42 如果我选择选项 1,那么实例化 Test 对象的正确方法是什么?我仍然在创建右值 std::map 的语法上遇到问题。
【解决方案2】:

是的,但您的构造函数采用左值引用。它必须是对const 的引用或右值引用。

就像任何其他类型一样。

【讨论】:

  • 如果我想使用对 const 的引用(而不是右值引用),正确的语法是什么?在定义构造函数 Test(std::map const& rMap) 之后,我仍在努力用右值 std::map 实例调用它。
  • @StoneThrow:“我在挣扎”实际上是什么意思?
  • "struggling" 这里特指继续编译错误。将构造函数更改为 Test(std::map const& rMap),我仍然无法使用 main() 中的三种方法中的任何一种来实例化 Test 对象。如果 Test 对象的构造函数采用了对 const 的引用,那么实例化一个 Test 对象的正确语法是什么?
  • 作为次要说明:除非这个问题只能通过右值引用来解决,否则我想用你建议的对 const 的引用来解决它。
  • @StoneThrow:在你告诉我们那些编译错误是什么,以及你的代码是什么样子之前,我不明白你的期望我们帮助您修复它们。 “我很挣扎”和“继续编译错误”并不能帮助我们帮助您。
【解决方案3】:
#include <map>

class Test {
public:
    Test(std::map<int,char> const& cMap)
    {
        std::map<int,char>::const_iterator iter;
        for (iter = cMap.cbegin(); iter != cMap.cend(); ++iter)
        {
            mMap[iter->first] = mMap[iter->second];
        }
    }
    virtual ~Test() { }
protected:
    std::map<int, char> mMap;
};

int main() {
    Test test(std::map<int,char>({{1,'a'}, {2, 'b'}}));
    return 0;
}

一些解释:

  1. 你需要Test(T)、Test(const T&amp;)或Test(T&amp;&amp;)构造函数,如果你想将临时对象(如std::map&lt;int,char&gt;())传递给它。不要忘记 T&amp;&amp; 构造函数的用途。如果有疑问,请不要使用它们。
  2. 如果您使用const std::vector/map/list/...,则不能使用.begin() 和.end() 来遍历元素——使用.cbegin() 和.c结束()。此外,使用auto 会变得更容易。试试看

    for (auto iter = rMap.cbegin(); iter != rMap.cend(); ++iter)
    

    改为

    std::map<int,char>::const_iterator iter;
    for (iter = rMap.cbegin(); iter != rMap.cend(); ++iter)
    
  3. 要初始化std::map,请使用“双括号”初始化器,例如{{1,'a'}, {2, 'b'}} -- {{key, value}, {second_key, second_value}, ...} 这种构造是可用的,因为

    1) std::map 有这个构造函数:

    map( std::initializer_list<value_type> init,
         const Compare& comp = Compare(),
         const Allocator& alloc = Allocator() );
    // see http://en.cppreference.com/w/cpp/container/map/map
    

    2) 内括号{1,'a'} 被解释为value_type 构造函数调用。而value_type 代表std::map&lt;int, char&gt; 是std::pair&lt;int, char&gt;。

P。 S. 不要忘记 std::map&lt;int,char&gt;() 和 std::map&lt;int,char&gt;{} 构造函数调用是相等的。这意味着你可以去掉前面的括号:std::map&lt;int,char&gt;({{1,'a'},{2,'b'}}) --&gt; std::map&lt;int,char&gt;{{1,'a'},{2,'b'}}

【讨论】:

  • 我喜欢你的散文和你的例子所说的几乎完全相反的东西。
  • @LightnessRacesinOrbit 好吧,问题作者想在构造函数中接受 r 值。我的示例解决了作者的问题,但我仍然想知道作者是否理解 r 值的使用位置。
  • 你会说这个答案可能会增加这种理解吗?
  • @LightnessRacesinOrbit Okey。好的。别紧张。我修正了我的答案。
  • @LightnessRacesinOrbit 我认为不需要任何 cmets,因为代码会自行显示。 const&amp; -- const 引用,const_iterator cbegin cend -- 使用 const&amp; 并使用“双括号”进行适当的地图构造{{1,'a'}, {2, 'b'}}
【解决方案4】:

你应该使用:

class Test
{
public:
    Test(const std::map<int,char>& rMap) : mMap(rMap)
    {}
    Test(std::map<int,char>&& rMap) : mMap(std::move(rMap))
    {}
    virtual ~Test(){}
protected:
    std::map<int, char> mMap;
};

这提供了两个构造函数:一个接受const 左值引用并将其内容复制到mMap,另一个接受右值引用并将其内容移动到mMap。这允许您使用命名的std::map 或临时构造一个Test 实例:

int main()
{
    std::map<int,char> m{{1, 'a'}, {2,'b'}};
    Test t1{m};
    Test t2{{{1, 'a'}, {2, 'b'}}};
}

这可以让您尽可能避免不必要的复制,但仍然可以让您在需要时复制map。

【讨论】:

  • 这样做的方法肯定是按值获取地图的单个重载吗?
  • @LightnessRacesinOrbit 人们(例如 Niebler)这么说,但如果这不会引起额外的移动就好了。
  • @Barry:一个移动实际上是免费的,即使它不能被优化掉(这种情况很少见)。比重复自己要好得多,除非你有某些情况下这真的是个问题。
猜你喜欢
  • 2021-02-24
  • 2012-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-10
  • 1970-01-01
  • 2011-06-28
相关资源
最近更新 更多