Вопрос или проблема
Рассмотрим следующий код –
#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>
рекомендуется использовать:
- Строковые литералы с суффиксом
s
, что позволяет избежать явного преобразования типов. - Переопределение функции для приема
const char(&)[N]
, что обеспечивает автопреобразование вstd::string
.
Эти подходы позволяют добиться более читабельного и ожидаемого поведения кода. Надеюсь, что данная информация поможет вам лучше разобраться с вашим вопросом. Если у вас возникнут дополнительные вопросы, не стесняйтесь задавать их!