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

Kly*_*pto 5 c++ operator-overloading c++11

我有一个类,它既具有到内在类型的隐式转换操作符(),又能够通过用于设置存储的字符串索引操作符[]进行访问。它在 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]);
        }
    }
};

Run Code Online (Sandbox Code Playgroud)

用法:

    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
Run Code Online (Sandbox Code Playgroud)

错误信息如下:

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

内置运算符“整数[对象指针]”函数

“设置::运算符[](std::字符串键)”

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

我理解为什么存在错误/警告,因为它来自于使用数组本身反转数组上的索引器的能力(这本身就是极其奇怪的语法,但对于指针算术来说具有逻辑意义)。

    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
Run Code Online (Sandbox Code Playgroud)

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

那可能吗?

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

Ker*_*g73 5

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

template<typename T>
operator T()
{
    return data;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

选项1

const char [9]消除从到 的隐式转换std::stringSetting::operator[]您可以通过创建一个接受字符数组引用(对字符串文字的引用)的模板来完成此操作。

template <size_t Size>
Setting &operator[](const char (&key)[Size]);
Run Code Online (Sandbox Code Playgroud)

选项2

Setting消除从到 的隐式转换int。您可以通过将用户定义的转换标记为 来完成此操作explicit

template <typename T>
explicit operator T() const;
Run Code Online (Sandbox Code Playgroud)

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

int x{data["TestNode"]};
Run Code Online (Sandbox Code Playgroud)

选项3

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

template <typename T>
T get() const;
Run Code Online (Sandbox Code Playgroud)

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

int x = data["TestNode"].get<int>();
Run Code Online (Sandbox Code Playgroud)

其他一些注意事项

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

template<typename T>
operator T() const {
    return data;
}
Run Code Online (Sandbox Code Playgroud)

我注意到的另一件事是:

std::shared_ptr<Setting>(new Setting())
Run Code Online (Sandbox Code Playgroud)

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

std::make_shared<Setting>()
Run Code Online (Sandbox Code Playgroud)

另一件事,我对你的设计了解不够,无法自己做出这个决定,但你真的需要使用吗std::shared_ptr?我不记得上次使用std::shared_ptrasstd::unique_ptr是什么时候了,它效率更高,并且在大多数情况下似乎就足够了。真的,你真的需要一个指针吗?使用std::shared_ptr<Setting>std::unique_ptr<Setting>超过有什么理由吗Setting?只是需要思考一下。