Почему мой вариант конвертирует std::string в bool?

Мой std::variant может быть пустым (std::monostate), содержат int, 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    
}

РЕДАКТИРОВАТЬ Так как пользователи моего кода не могут явно сделать strings, мне нравится ловить это. В противном случае это будет источником ошибки. Я сделал вспомогательную функцию шаблона, которая проверяет, 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);    
}

2 ответа

Решение

Тип "hello" является const char [6]который разлагается на const char *, Преобразование из const char * в bool является встроенным преобразованием, а преобразование из const char * в std::string является определяемым пользователем преобразованием, что означает, что первое выполняется.

Поскольку вы используете C++ >= 14, вы можете использовать буквальный суффикс s обозначить std::string буквальный:

using namespace std::string_literals;

var contains_string = "hello"s;

"hello" не является std::string, это const char * и он может быть неявно преобразован в bool, а также использоваться для создания std::string,
Другими словами, это работает просто отлично:

int main() {
    bool b = "foo";
    (void)b;
}

Как упомянуто в комментариях @aschepler:

Неявное преобразование из const char* в bool предпочтительнее пользовательского преобразования из const char* в std::string, int это не вариант вообще.

Другие вопросы по тегам