Boost:: variant - почему "const char *" преобразован в "bool"? - программирование
Подтвердить что ты не робот

Boost:: variant - почему "const char *" преобразован в "bool"?

Я объявил boost::variant, который принимает три типа: string, bool и int. Следующий код показывает, что мой вариант принимает const char* и преобразует его в bool. Является ли обычное поведение для boost::variant для принятия и преобразования типов, не входящих в его список?

#include <iostream>
#include "boost/variant/variant.hpp"
#include "boost/variant/apply_visitor.hpp"

using namespace std;
using namespace boost;

typedef variant<string, bool, int> MyVariant;

class TestVariant
    : public boost::static_visitor<>
{
public:
    void operator()(string &v) const
    {
        cout << "type: string -> " << v << endl;
    }
    template<typename U>
    void operator()(U &v)const
    {
        cout << "type: other -> " << v << endl;
    }
};

int main(int argc, char **argv) 
{
    MyVariant s1 = "some string";
    apply_visitor(TestVariant(), s1);

    MyVariant s2 = string("some string");
    apply_visitor(TestVariant(), s2);

    return 0;
}

выход:

Тип: другое → 1
type: string → some string

Если я удалю тип bool из MyVariant и измените его на это:

typedef variant<string, int> MyVariant;

const char* больше не преобразуется в bool. На этот раз он преобразуется в string, и это новый вывод:

type: string → некоторая строка
 type: string → some string

Это означает, что variant пытается сначала преобразовать другие типы в bool, а затем в string. Если преобразование типа является чем-то неизбежным и должно всегда происходить, есть ли способ дать преобразование в string более высокий приоритет?

4b9b3361

Ответ 1

Я не думаю, что это что-то особенное для boost::variant, это о том, какой конструктор выбирается с помощью разрешения перегрузки. То же самое происходит с перегруженной функцией:

#include <iostream>
#include <string>

void foo(bool) {
    std::cout << "bool\n";
}

void foo(std::string) {
    std::cout << "string\n";
}

int main() {
    foo("hi");
}

выход:

bool

Я не знаю, как изменить, какие конструкторы есть у Variant [edit: как говорит Джеймс, вы можете написать другой класс, который использует вариант в своей реализации. Затем вы можете предоставить конструктор const char*, который делает правильные вещи.]

Возможно, вы можете изменить типы в Варианте. Другой пример перегрузки:

struct MyBool {
    bool val;
    explicit MyBool(bool val) : val(val) {}
};

void bar(MyBool) {
    std::cout << "bool\n";
}

void bar(const std::string &) {
    std::cout << "string\n";
}

int main() {
    bar("hi");
}

выход:

string

К сожалению, теперь вы должны написать bar(MyBool(true)) вместо foo(true). Еще хуже в случае вашего варианта с string/bool/int, если вы просто измените его на вариант string/MyBool/int, тогда MyVariant(true) вызовет конструктор int.

Ответ 2

Это не имеет ничего общего с boost::variant, но с порядком, в котором C++ выбирает преобразования для применения. Прежде чем пытаться использовать пользовательские преобразования (помните, что std::string является пользовательским классом для этой цели), компилятор попробует встроенные преобразования. Нет встроенного преобразования из const char* в int, но в соответствии с §4.12 стандарта:

Значение типа указателя [...] типа [...] можно преобразовать в значение типа bool.

Таким образом, компилятор с радостью преобразует ваш const char* в bool и никогда не задумывается о том, чтобы преобразовать его в std::string.

ОБНОВЛЕНИЕ: похоже, что это явно нежелательное преобразование исправлено. Техническое объяснение исправления вы можете найти здесь.