【问题标题】:How to use operator+ and operator= together?如何同时使用 operator+ 和 operator=?
【发布时间】:2013-04-22 08:17:39
【问题描述】:

我班上的 2 个运算符有点问题。

我的班级被宣布:

template <class keyType, class valueType>
class MyMap{

    keyType keys[MAX];
    valueType values[MAX];
    int size;
}

我需要重新定义运算符 [],例如当我调用时:std::cout&lt;&lt;MyMap1["a"];。

keyType& operator[] (keyType key){}

它工作正常。我也用它来做作业,很好,例如:MyMap1["a"]="a1";

代码很完美。但是在我的operator[] 中,我增加了size 参数,它仅在我想进行分配时才有用。当我只做std::cout时,我不想增加它。

所以也许我应该重新定义 operator= 函数但是当我不能写时:

void operator=(valueType value){}

因为MyMap1["a"] = "a1" 的左成员是keyType 而不是MyMap 类型。

我该怎么办?

【问题讨论】:

  • 如果你的类是一个 K->V 映射,那么 operator[] 应该被定义为valueType&amp; operator[] (keyType key),因为当你写map[key]时你通常想读/写存储在给定键下的价值事物..
  • 没错!但是这两种类型是字符串,所以我没有看到那个错误。对我的问题有什么想法吗?

标签: c++ operators redefinition


【解决方案1】:

问题本身不在于输出;这就是你在 钥匙不存在。我认为您指定非常重要 在走得更远之前。有很多可能性:

  • 你可以做std::map做的事,并用默认值插入 价值。这在很多情况下非常方便,但这确实意味着 你不能在 const 映射上使用 []。

  • 你可以让操作符返回一个指针,带有一个空值 对象不存在时的指针。就个人而言,我不喜欢 这是operator[];这意味着你必须写东西 喜欢:ValueType* p = myMap[ key ]; if ( p != NULL ) ...,其中 [] 似乎不太自然。 (另一方面,它确实有效 find 或 get 函数。)

  • 你可以抛出异常(或者甚至使用断言,如果你 提供contains 函数,并将其作为 [])。

  • 您可以返回预定义的默认值。这是一种 与第一种解决方案相反;这意味着operator[] 永远不会改变地图,也没有必要 一个非常量版本。

  • 最后(这似乎是您的目标):您 可以让operator[] 返回一个代理,以便区分 在用作右值的myMap["a"] 和myMap["a"] = something 之间。我的感觉是这个解决方案不适合 C++ 的工作方式很好,但它也用于其他语言 (例如,像 Python)。

对于最后一个解决方案,您可以使用类似:

class MyMap
{
    class Proxy
    {
        MyMap* myOwner;
        KeyType myKey;
    public:
        Proxy( MyMap* owner, KeyType key )
            : myOwner( owner )
            , myKey( key )
        {
        }

        Proxy const& operator=( ValueType value ) const
        {
            myOwner->insertOrUpdate( myKey, value );
            return *this;
        }

        operator ValueType() const
        {
            if ( !myOwner->contains( myKey ) ) {
                throw UnknownKeyException( myKey );
            }
            return myOwner->get( myKey );
        }
    };

public:
    Proxy operator[]( KeyType key )
    {
        return Proxy( this, key );
    }
};

【讨论】:

  • Python 比这更神奇——如果存在__setitem__,P​​ython 运行时会将myMap["a"] = "b" 转换为myMap.__setitem__("a", "b")。所以你通常不需要代理对象AFAIK。我可以相信它们在 Python 中的用途就像在 C++ 中一样,但是对容器元素的普通分配不是其中之一:-)
  • 另外,我想知道在 C++ 中是否存在组合 (1) 和 (3) 的参数。也就是说,如果缺少键,让const V &amp;operator[](const K&amp;)const 抛出异常,而非常量operator[] 返回一个非常量引用并在缺少键时插入一个条目。以两种不同的方式处理相同的错误通常是一个坏主意,但在这种情况下,它可能与常见用法相匹配,并且允许在 const Mymap 上使用 operator[] 以供那些想要在更高级别捕获的人使用。
  • @SteveJessop 是的。我说的是标准 Python map;除了能够重新定义__setitem__(通过从map 派生)之外,还有__missing__,如果找不到对象,它将在读取时调用。这里重要的一点是,Python 确实区分了读写,这是我们需要在 C++ 中代理的。
  • @SteveJessop 关于结合(1)和(3),我有点怀疑。这似乎太像尝试使用一个运算符来满足两种不同的要求。而且我不喜欢如果有人添加最初被遗忘的const,行为会改变的想法。
  • 我完全同意:仅由 'const' 限定符不同的重载应该与非 const 限定符一致。 'Const' 表示编译时错误检查。将 const/nonconst 中的行为混合为 (1)+(3) 组合,这比你说的更糟糕。通常,在此处和那里添加“const”作为编译时错误检查。预计如果将其放置在不适用的地方,那么编译将立即中断。在运行时添加一个“常量”来抛出异常是......就像传递零的东西一样邪恶。
【解决方案2】:

如果你想区分读写操作,解决方案是从operator []返回一个代理对象。

例子见https://stackoverflow.com/a/16132858/320726

【讨论】:

    【解决方案3】:

    “但是在我的 operator[] 中,我增加了 size 参数”。

    不要。检查密钥是否存在,只有在插入新密钥时才增加size。当您执行std::cout 时,大概您只是在现有的键/值对上执行此操作。

    [编辑] 鉴于评论,这是一个替代解决方案。在operator[] 中,返回MyMap&lt;KeyType, ValueType&gt;::proxy。这包含了 MyMap&amp; 和 Key。重载MyMap::proxy::operator=(ValueType) 以分配新值,并增加size。重载MyMap::proxy::operator ValueType() const 以获取值。如果密钥不存在,则返回 ValueType()。

    【讨论】:

    • 我复制/粘贴了我告诉 Paul 的相同回复,但我想 std::cout 一个不在我班上的密钥,我想输出一个 std::endl
    • 我提供了一个链接,你可以从那里借用很多代码
    【解决方案4】:

    当你写作时:

    auto blah = map[key];
    

    operator[] 使用key 调用,并返回一个值。

    当你写作时:

    map[key] = blah;
    

    然后使用key 调用operator[key],并返回一个值,然后使用blah 参数对值调用operator=。

    这意味着可能很难检测到您实际在哪里读取或写入地图。

    但是,通常,在 READ 案例中,案例可能会退回到调用的 value const&amp; operator[] const,而在写入期间,它会是 value&amp; operator[] 没有 const 标记。因此,您可能尝试通过 const 说明符重载运算符:提供 2 个 operator[]、一个 const、一个 non-const,并仅在后者中增加大小。

    这很简单,但并不总是能正常工作,因为您有时可能会意外调用 'operator[] as read' 但没有编译器可确定的 const 约束.我目前无法确切说明何时、如何以及是否有可能,但我想如果你对 constness 不太小心的话,你可以很容易地做到这一点。

    如果你做到了,那么我知道的唯一选择就是为非常量模式下的返回值提供一个包装器,并为 const 模式保留正常:

    myMap::valueType const & operator[](key...) const
    mymap::wrapper<myMap::valueType>& operator[](key...)
    

    您的包装器会记住您的 Map 的 ref& 并会记住 KEY,并且该包装器将提供隐式转换到 valueType,并且它将提供赋值运算符 FROM-valueType-TO-wrappertype。隐式转换为值类型将执行从映射到给定键的读取,并且不会增加计数器,而运算符=(从值类型,到包装类型)将执行对映射的写入。

    这肯定行得通,但这种方法可能已经过时。我不熟悉 c'11 的更改,所以现在可能有一些更好的选项可用 - 例如,移动 &amp;&amp; 语义可能会在这里改变一些东西,即可能是过载

     valueType&& operator[](key)
    

    也有可能——但我不知道。我只知道 return-a-transparent-wrapper 方法。

    编辑:这是一个很好的(似乎完整的)示例,它支持读取和写入并区分这两者:

    stack: How to do some stuff with values assigned in subscript operator?

    【讨论】:

      【解决方案5】:

      只有当key 不存在于keys 中时,您才会增加size,对吗?这就是std::map 所做的。因此,如果您只是打印地图的内容,您将读取现有键的值,因此 size 不会增加。

      【讨论】:

      • 你说得对,我只在 key 不存在时才增加大小,你说得对,我尝试重写 std::map 类。但我想 std::cout 一个不在我班上的钥匙,我想输出一个 std::endl
      • (cut:was wrong),这就是问题所在:他不想在使用 operator[] 阅读时增加它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-14
      • 1970-01-01
      • 2014-01-08
      • 1970-01-01
      相关资源
      最近更新 更多