Как предотвратить преобразование bool в int в конструкторе? - PullRequest
4 голосов
/ 28 февраля 2020

У меня есть следующий класс, максимально приближенный к моему производственному коду:

#include <iostream>

template <typename T>
struct M {

    M(std::string a, std::string b, T value = T(), const bool ready = false) : m_value{value}, m_ready{ ready } {}

    T m_value;
    bool m_ready;

};

auto main() -> int {
    {
        M<int> m{"a", "b"};
        std::cerr << m.m_value << std::endl;
    }
    {
        M<int> m{"a", "b", true};
        std::cerr << m.m_value << std::endl;
    }
}

В первом случае значение m_value равно 0, как и ожидалось. Во втором это 1, так как он принимает значение bool. Есть ли способ избежать конверсии?

Ответы [ 3 ]

5 голосов
/ 28 февраля 2020

Вы можете предотвратить преобразование, явно удалив версию, которая непосредственно принимает bool в качестве третьего параметра:

M(std::string, std::string, bool, bool = false) = delete;

Однако, если T равно bool, это вызовет проблемы. Таким образом, вам нужно использовать некоторую гимнастику SFINAE, чтобы это определение появилось только тогда, когда T конвертируется в bool, но не на самом деле bool.

3 голосов
/ 28 февраля 2020

Вы можете добавить другой конструктор, чтобы отклонить все, что не совсем T:

template <typename T>
struct M {
    M(std::string a, std::string b, T value = T(), const bool ready = false);

    template <typename U>
    M(std::string, std::string, U, bool = false) = delete;
};

M<int>("hello", hello", true) предпочтет шаблон конструктора, который будет удален. Но обратите внимание, что так будет и M<int>("hello", "hello", '4'), а также M<int>("hello", "hello", 4u). Так что это вопрос действительно проработки, какие именно вещи вы хотите удалить.

Если вы буквально хотите отклонить только bool, вы можете сделать это, ограничив шаблон:

template <typename U, std::enable_if_t<std::is_same_v<U, bool>, int> = 0>
M(std::string, std::string, U, bool = false) = delete;

Или в C ++ 20:

template <std::same_as<bool> U>
M(std::string, std::string, U, bool = false) = delete;

или:

M(std::string, std::string, std::same_as<bool> auto, bool = false) = delete;

Если сделать это таким образом, можно будет конструировать M<bool> из двух string s и bool, поскольку конструктор без шаблона все равно будет лучше матч.

3 голосов
/ 28 февраля 2020

Я думаю, что это работает

#include <iostream>
#include <type_traits>

template <typename T>
struct M {

    template <typename U=T, typename=std::enable_if_t<std::is_same_v<T, U>>>
    M(std::string a, std::string b, U value = U(), const bool ready = false) : m_value{value}, m_ready{ ready } {}

    T m_value;
    bool m_ready;

};

auto main() -> int {
    {
        M<int> m{"a", "b"};
        std::cout << m.m_value << std::endl;
    }
    {
        M<int> m{"a", "b", 1};
        std::cout << m.m_value << std::endl;
    }
    {
        // This does not compile
        // M<int> m{"a", "b", true};
        // std::cout << m.m_value << std::endl;
    }
    {
        // This compiles
        M<bool> m{"a", "b", true};
        std::cout << m.m_value << std::endl;
    }
    return 0;
}
...