I would like to do the following:
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" });
using the templates
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);
}
but I get the following error (using GCC 9.3.0):
no matching function for call to ‘is_in(std::string&, <brace-enclosed initializer list>)’
Any big brain template fellas out there got suggestions?
CodePudding user response:
For is_in(b, { "a", "b", "c" });
, template parameter Element
is deduced as std::string
on the 1st argument b
, and deduced as const char*
on the 2nd argument { "a", "b", "c" }
; they don't match.
You can give two template parameters for is_in
, e.g.
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);
}
Or use std::type_identity
(since C 20; and it's quite easy to write one for pre-C 20) to exclude the 2nd function parameter from type deduction.
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);
}