Я бы хотел сделать следующее:

std::string b = "b";
is_in("a", { "a", "b", "c" });
is_in("d", { "a", "b", "c" });
is_in(b, { "a", "b", "c" }); // fails
is_in(b, std::array{ "a", "b", "c" });

Используя шаблоны

template<typename Element, typename Container>
bool is_in(const Element& e, const Container& c)
{
    // https://stackoverflow.com/questions/20303821/how-to-check-if-string-is-in-array-of-strings
    return std::find(std::begin(c), std::end(c), e) != std::end(c);
}

template<typename Element>
bool is_in(Element e, std::initializer_list<Element> l)
{
    // return std::find(std::begin(l), std::end(l), e) != std::end(l);
    return is_in<Element, std::initializer_list<Element>>(e, l);
}

Но я получаю следующую ошибку (используя GCC 9.3.0):

no matching function for call to ‘is_in(std::string&, <brace-enclosed initializer list>)’

У каких-нибудь больших мозговых шаблонов есть предложения?

3
Androvich 15 Окт 2021 в 10:51

2 ответа

Лучший ответ

Для is_in(b, { "a", "b", "c" }); параметр шаблона Element выводится как std::string для 1-го аргумента b и выводится как const char* для 2-го аргумента { "a", "b", "c" }; они не совпадают.

Вы можете указать два параметра шаблона для is_in, например

template<typename E1, typename E2>
bool is_in(E1 e, std::initializer_list<E2> l)
{
    // return std::find(std::begin(l), std::end(l), e) != std::end(l);
    return is_in<E1, std::initializer_list<E2>>(e, l);
}

Или используйте std::type_identity (начиная с C ++ 20; и это довольно легко написать один для pre-C ++ 20), чтобы исключить второй параметр функции из вывода типа.

template<typename Element>
bool is_in(Element e, std::initializer_list<std::type_identity_t<Element>> l)
{
    // return std::find(std::begin(l), std::end(l), e) != std::end(l);
    return is_in<Element, std::initializer_list<Element>>(e, l);
}
4
songyuanyao 15 Окт 2021 в 08:08

Другой подход, который включает преобразование несоответствующих строковых типов в std :: string перед их сравнением.

#include <cassert>
#include <array>
#include <string>

// Help the compiler figure out to compare "unrelated" string types
namespace details
{
    template<typename type_t>
    struct compare_as
    {
        using type = type_t;
    };

    template<std::size_t N>
    struct compare_as<char[N]>
    {
        using type = std::string;
    };

    template<>
    struct compare_as<char*>
    {
        using type = std::string;
    };
}

// template for "array" style parameters 
template<typename type_t, typename coll_t, std::size_t N>
constexpr auto is_in(const type_t& value, const coll_t(&values)[N])
{
    for (const auto& v : values)
    {
        typename details::compare_as<coll_t>::type lhs{ v };
        typename details::compare_as<type_t>::type rhs{ value };
        if (lhs == rhs) return true;
    }

    return false;
}

// template for containers
template<typename type_t, typename coll_t>
constexpr auto is_in(const type_t& value, const coll_t& values)
{
    for (const auto& v : values)
    {
        typename details::compare_as<type_t>::type lhs{ v };
        typename details::compare_as<type_t>::type rhs{ value };
        if (lhs == rhs) return true;
    }

    return false;
}

int main()
{
    // for non-string types compile time checking is possible
    static_assert(is_in(1, { 1,2,3 }));

    std::string b = "b";
    assert(is_in("a", { "a", "b", "c" }));
    assert(!is_in("d", { "a", "b", "c" }));
    assert(is_in(b, { "a", "b", "c" }));
    assert(is_in(b, std::array{ "a", "b", "c" }));
}
0
Pepijn Kramer 15 Окт 2021 в 09:15