【问题标题】:Templated is_in() function (check if array contains string) in C++C++ 中的模板化 is_in() 函数(检查数组是否包含字符串)
【发布时间】:2021-12-03 11:13:06
【问题描述】:

我想做以下事情:

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>)’

有没有什么大脑模版的小伙伴有建议?

【问题讨论】:

    标签: c++ arrays string templates gcc


    【解决方案1】:

    对于is_in(b, { "a", "b", "c" });,模板参数Element在第一个参数b上推导出为std::string,在第二个参数{ "a", "b", "c" }上推导出为const char*;他们不匹配。

    你可以给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 起;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);
    }
    

    【讨论】:

    • 哦,这很有道理,非常感谢!你能链接我如何重写 std::type_identity 吗?
    • @Androvich:链接提供了可能的实现。
    【解决方案2】:

    另一种方法是在比较之前将不匹配的字符串类型转换为 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" }));
    }
    

    【讨论】:

    • 哦,这很整洁。但是保持它更通用,这样它可以与原语和 std::string 一起使用不是更好吗?或者 std::find 实际上可以使用的任何类型。
    • 可能是的 :) 正如所说,这只是几分钟的努力。而且大多数类型实际上是可比较的,只是 c++ 中的字符数组(字符串文字)太乱了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-01
    • 2012-03-27
    • 1970-01-01
    • 2015-02-07
    • 2022-06-30
    • 2021-06-10
    • 2011-02-24
    相关资源
    最近更新 更多