более чем один оператор "[]" соответствует этим операндам - PullRequest
3 голосов
/ 05 февраля 2020

У меня есть класс, который имеет как неявный оператор преобразования () в intrinsi c типы, так и возможность доступа с помощью оператора строкового индекса [], который используется для хранилища настроек. Он компилируется и очень хорошо работает в модульных тестах на g cc 6.3 и MSV C, однако класс вызывает некоторые предупреждения о неоднозначности для intellisense и 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 [указатель на объект] "функция

" Setting :: operator [] (std :: string key) "

Типы операндов: Установка [const char [15]]

Я понимаю, почему возникает ошибка / предупреждение, так как она связана с возможностью обращения индексатора к массиву с самим массивом (который сам по себе является чрезвычайно причудливым синтаксисом, но имеет логический смысл с указателем arithmeti c).

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

Я не уверен, как избежать появления сообщения об ошибке при сохранении того, что я хочу выполнить sh , (неявное присваивание и индексирование по строке)

Возможно ли это?

Примечание: я не могу использовать функции C ++ выше 11.

1 Ответ

3 голосов
/ 05 февраля 2020

Проблема заключается в заданном пользователем шаблоне функции неявного преобразования.

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

Когда компилятор учитывает выражение data["TestNode"], необходимо выполнить некоторые неявные преобразования. Компилятор имеет две опции:

  • Преобразование const char [9] в const std::string и вызов Setting &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

Устраните неявное преобразование от Setting до int. Это можно сделать, пометив пользовательское преобразование как explicit.

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

. Для этого потребуется обновить код вызова, чтобы использовать прямую инициализацию вместо инициализации копирования.

int x{data["TestNode"]};

Опция 3

Устранить неявное преобразование из Setting в int. Другой способ сделать это - полностью удалить пользовательское преобразование и использовать функцию.

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<Setting> или std::unique_ptr<Setting> вместо Setting? Просто о чем подумать.

...