【问题标题】:more than one operator "[]" matches these operands多个运算符 "[]" 匹配这些操作数
【发布时间】:2020-05-20 21:55:33
【问题描述】:

我有一个类,它既具有隐式转换 operator() 到内在类型的能力,又具有通过用于设置存储的字符串索引 operator[] 进行访问的能力。它在 gcc 6.3 和 MSVC 的单元测试中编译和工作得非常好,但是该类在智能感知和 clang 上会引起一些不明确的警告,这是不可接受的。

超级瘦身版: https://onlinegdb.com/rJ-q7svG8

#include <memory>
#include <unordered_map>
#include <string>


struct Setting
{
    int data; // this in reality is a Variant of intrinsic types + std::string
    std::unordered_map<std::string, std::shared_ptr<Setting>> children;
    template<typename T>
    operator T()
    {
        return data;
    }
    template<typename T>
    Setting & operator=(T val)
    {
        data = val;
        return *this;
    }
    Setting & operator[](const std::string key)
    {
        if(children.count(key))
            return *(children[key]);
        else
        {
            children[key] = std::shared_ptr<Setting>(new Setting());
            return *(children[key]);
        }
    }
};

用法:

    Setting data;
    data["TestNode"] = 4;
    data["TestNode"]["SubValue"] = 55;
    int x = data["TestNode"];
    int y = data["TestNode"]["SubValue"];
    std::cout << x <<std::endl;
    std::cout << y;
    
output:
4
55

错误信息如下:

多个运算符“[]”匹配这些操作数:

内置运算符“integer[pointer-to-object]”函数

"设置::操作符[](std::string key)"

操作数类型有:设置 [ const char [15] ]

我理解为什么会出现错误/警告,因为它能够使用数组本身来反转数组上的索引器(这本身就是一种非常奇怪的语法,但对于指针算术具有逻辑意义)。

char* a = "asdf";
char b = a[5];
char c = 5[a];
b == c

我不确定如何避免它呈现的错误消息,同时保持我想要完成的任务。 (隐式赋值和字符串索引)

这可能吗?

注意:我不能使用 11 以上的 C++ 功能。

【问题讨论】:

    标签: c++ c++11 operator-overloading


    【解决方案1】:

    问题是用户定义的隐式转换函数模板。

    template<typename T>
    operator T()
    {
        return data;
    }
    

    当编译器考虑表达式data["TestNode"] 时,需要进行一些隐式转换。编译器有两种选择:

    • const char [9] 转换为const std::string 并调用Setting &amp;Setting::operator[](const std::string)
    • Setting 转换为int 并调用const char *operator[](int, const char *)

    这两个选项都涉及隐式转换,因此编译器无法确定哪个更好。编译器说调用不明确。

    有几种方法可以解决这个问题。

    选项 1

    消除从const char [9]std::string 的隐式转换。为此,您可以将Setting::operator[] 设为接受对字符数组的引用(对字符串文字的引用)的模板。

    template <size_t Size>
    Setting &operator[](const char (&key)[Size]);
    

    选项 2

    消除从Settingint 的隐式转换。您可以通过将用户定义的转换标记为explicit 来做到这一点。

    template <typename T>
    explicit operator T() const;
    

    这将要求您更新调用代码以使用直接初始化而不是复制初始化。

    int x{data["TestNode"]};
    

    选项 3

    消除从Settingint 的隐式转换。另一种方法是完全删除用户定义的转换并使用函数。

    template <typename T>
    T get() const;
    

    显然,这也需要您更新调用代码。

    int x = data["TestNode"].get<int>();
    

    其他一些说明

    我注意到代码的一些事情是您没有将用户定义的转换标记为const。如果成员函数不修改对象,则应将其标记为 const 以便能够在常量对象上使用该函数。所以把const放在参数列表后面:

    template<typename T>
    operator T() const {
        return data;
    }
    

    我注意到的另一件事是:

    std::shared_ptr<Setting>(new Setting())
    

    您在这里提到了两次Setting,并在您可以做一次时进行了两次内存分配。代码清洁度和性能最好改为这样做:

    std::make_shared<Setting>()
    

    还有一件事,我对你的设计了解不多,无法自己做出决定,但你真的需要使用std::shared_ptr吗?我不记得我上次使用std::shared_ptr 是什么时候了,因为std::unique_ptr 效率更高,而且在大多数情况下似乎就足够了。真的,你需要一个指针吗?是否有任何理由使用std::shared_ptr&lt;Setting&gt;std::unique_ptr&lt;Setting&gt; 而不是Setting?只是想一想。

    【讨论】:

    • 谢谢,选项 1 似乎可以解决。 shared_ptr 是因为该系统是否需要对子树进行某种形式的浅拷贝共享存在一些歧义。但是你的权利,它在技术上不需要指针。
    猜你喜欢
    • 2013-04-21
    • 1970-01-01
    • 2012-05-07
    • 2013-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多