Почему мой вариант конвертирует std::string в bool?
My std::variant
может быть пустым (std::monostate
), содержать int
, a std::string
или bool
.
Когда я хочу передать его строкой, заданной как var = "this is my string"
, она преобразуется в bool
, а не в строку. Если я объявляю тип явно, он работает var = std::string("this is my string")
. Почему это и есть что-то, что я могу сделать, чтобы избежать этого?
#include <string>
#include <variant>
#include <iostream>
int main()
{
using var = std::variant<std::monostate, int, std::string, bool>;
var contains_nothing;
var contains_int = 5;
var contains_string = "hello";
var contains_expl_string = std::string("explicit hello");
var contains_bool = false;
auto visitor = [](auto&& arg){
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same<T, std::monostate>())
std::cout<<"nothing\n";
else if constexpr (std::is_same<T, int>())
std::cout<<"int: "<<arg<<"\n";
else if constexpr (std::is_same<T, std::string>())
std::cout<<"string: "<<arg<<"\n";
else if constexpr (std::is_same<T, bool>())
std::cout<<"bool: "<<arg<<"\n";
else
std::cout<<"Visitor is not exhaustive\n";
};
std::visit(visitor, contains_nothing); // nothing
std::visit(visitor, contains_int); // int: 5
std::visit(visitor, contains_string); // bool: 1
std::visit(visitor, contains_expl_string); // string: explicit hello
std::visit(visitor, contains_bool); // bool: 0
}
ИЗМЕНИТЬ
Поскольку пользователи моего кода могут явно не делать string
s, мне это нравится. В противном случае это будет источником ошибок. Я создал функцию-шаблон, которая проверяет, прошел ли char*
, и если да, то делает std::string. Работает хорошо. Помогите сделать это проще, спасибо!
РЕДАКТИРОВАТЬ 2
Объявив std::monostate
как параметр/тип по умолчанию, он даже работает, когда make_var
вызывается без каких-либо аргументов.
#include <string>
#include <variant>
#include <iostream>
using var = std::variant<std::monostate, int, std::string, bool>;
template<typename T = std::monostate>
var make_var(T value = std::monostate())
{
if constexpr (std::is_same<typename std::remove_const<typename std::decay<T>::type>::type, const char*>())
return std::string(value);
return value;
}
int main()
{
auto contains_nothing = make_var();
auto contains_int = make_var(3);
auto contains_string = make_var("hello");
auto contains_expl_string = make_var(std::string("excplicit hello"));
var contains_bool = make_var(false);
auto visitor = [](auto&& arg){
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same<T, std::monostate>())
std::cout<<"nothing\n";
else if constexpr (std::is_same<T, int>())
std::cout<<"int: "<<arg<<"\n";
else if constexpr (std::is_same<T, std::string>())
std::cout<<"string: "<<arg<<"\n";
else if constexpr (std::is_same<T, bool>())
std::cout<<"bool: "<<arg<<"\n";
else
std::cout<<"Visitor is not exhaustive\n";
};
std::visit(visitor, contains_nothing);
std::visit(visitor, contains_int);
std::visit(visitor, contains_string);
std::visit(visitor, contains_expl_string);
std::visit(visitor, contains_bool);
}
Ответы
Ответ 1
Тип "hello"
равен const char [6]
, который распадается на const char *
. Преобразование из const char *
в bool
является встроенным преобразованием, тогда как преобразование из const char *
в std::string
является пользовательским преобразованием, что означает, что выполняется первое.
Поскольку вы используете С++ >= 14, вы можете использовать буквенный суффикс s
для обозначения литерала std::string
:
using namespace std::string_literals;
var contains_string = "hello"s;
Ответ 2
"hello"
не является std::string
, это a const char *
, и он может быть неявно преобразован в bool, а также использован для построения std::string
.
Другими словами, это работает отлично:
int main() {
bool b = "foo";
(void)b;
}
Как упоминалось в комментариях @aschepler:
Неявное преобразование с const char*
в bool
является предпочтительным по сравнению с пользовательским преобразованием от const char*
до std::string
. int
не является вообще вариантом.