Предпочитайте std::string в std::variant для const char *

Вопрос или проблема

Рассмотрим следующий код –

#include <variant>
#include <string>

int p(std::variant<bool, std::string> v) {
    return v.index();
}

int main() {
    return p("ad");
}

Вместо выбора std::string, p будет инстанцироваться с вариантом, содержащим bool (я хочу std::string), это можно исправить, явно указав std::string, но это чересчур много работы 😊, я пытался предоставить различные перегрузки, но это, похоже, не работает.

Это было исправлено в C++20. См. P0608 для обсуждения этого конкретного случая.

Вы можете использовать суффикс строкового литерала s для создания объекта std::string напрямую

return p("ad"s);

Если суффикс недоступен, то вам нужно добавить using namespace std::literals, using namespace std::string_literals или using namespace std::literals::string_literals

Тем не менее, я протестировал ваш код на Wandbox, и p("ad") возвращает 1 для меня

Такой код:

#include <variant>
#include <string>

int p(std::variant<bool, std::string> v) {
    return v.index();
}

template<size_t N>
int p(const char (&s)[N]) {
    return p(std::string(s));
}

int main() {
    return p("ad");
}

возвращает 1.

Ответ или решение

Вопрос, который вы подняли, касается работы с std::variant и инициализации его std::string элементом при передаче литеральной строки типа const char*. Для более глубокого понимания ситуации, давайте рассмотрим несколько ключевых аспектов.

Проблема с инициализацией std::variant

Когда вы пишете p("ad"), компилятор видит строку как const char*, и в результате первый p привязывается к функции, ожидающей variant<bool, std::string>. Это приводит к тому, что компилятор по умолчанию создает std::variant с первым типом – bool, что в этом случае не является ожидаемым поведением.

Решение проблемы в C++20

Как было отмечено в вашем материалах, данная проблема была решена в стандарте C++20 благодаря изменению правил инициализации для std::variant. Однако, для проектов, использующих более ранние версии C++, существует несколько обходных путей.

Использование литералов строк

Один из лучших способов избежать этой сложности – это воспользоваться суффиксом литерала строк, который позволяет создавать объекты std::string напрямую из литералов. Это можно сделать так:

return p("ad"s);

Для этого вам потребуется добавить using namespace std::literals или аналогичный using, чтобы получить доступ к строковым литералам.

Переопределение функции

Другой подход – это создание специального перегруженного варианта функции, который принимает параметр типа const char(&s)[N]:

template<size_t N>
int p(const char (&s)[N]) {
    return p(std::string(s));
}

Этот метод также будет работать, обеспечивая соответствующее преобразование в std::string. Теперь, когда вы вызываете p("ad"), компилятор будет использовать перегруженную версию функции, что инициализирует std::variant<bool, std::string> через std::string.

Заключение

Таким образом, для обработки литеральных строк в контексте std::variant<bool, std::string> рекомендуется использовать:

  1. Строковые литералы с суффиксом s, что позволяет избежать явного преобразования типов.
  2. Переопределение функции для приема const char(&)[N], что обеспечивает автопреобразование в std::string.

Эти подходы позволяют добиться более читабельного и ожидаемого поведения кода. Надеюсь, что данная информация поможет вам лучше разобраться с вашим вопросом. Если у вас возникнут дополнительные вопросы, не стесняйтесь задавать их!

Оцените материал
Добавить комментарий

Капча загружается...