为什么带有 initializer_list 参数的模板与字符串不一致?
Why a template with an initializer_list argument misbehaves with string?
在 C++ Most efficient way to compare a variable to multiple values? 之后,我正在尝试使用 initializer_list
作为参数构建一个模板函数。当我只使用字符串时就会出现问题。我有以下代码:
functions.hpp
template <typename T>
bool is_in(T const& val, initializer_list<T> const& liste)
{
for (const auto& i : liste)
if (val == i) return true;
return false;
};
main.cpp
#include "functions.hpp"
using namespace std;
int main()
{
string test("hello");
if (is_in(test, {"foo", "bar"}))
cout << "good" << endl;
else
cout << "bad" << endl;
return 0;
}
我收到以下错误:
main.cpp: In function ‘int main()’:
main.cpp:18:34: error: no matching function for call to ‘is_in(std::string&, <brace-enclosed initializer list>)’
main.cpp:18:34: note: candidate is:
In file included from personnage.hpp:11:0,
from main.cpp:1:
functions.hpp:16:6: note: template<class T> bool is_in(const T&, const std::initializer_list<_Tp>&)
functions.hpp:16:6: note: template argument deduction/substitution failed:
main.cpp:18:34: note: deduced conflicting types for parameter ‘_Tp’ (‘std::basic_string<char>’ and ‘const char*’)
我不明白的是:当我在 main
中使用 int
或 double
而不是 string
时,一切都非常顺利...一个快速而肮脏的修复是将 is_in
声明为仅字符串的函数,但它不是很令人满意。
有谁知道如何保持模板和使用字符串一样吗?
答案很简单:
编译器不知道是否应该使用字符串中的 const char*
或 std::string
。
如果你写
if (is_in(test, {std::string("foo"), std::string("bar")}))
它按预期工作。
这也是您的编译器给您的提示:
main.cpp:18:34: note: deduced conflicting types for parameter ‘_Tp’ (‘std::basic_string’ and ‘const char*’)
在你的函数模板中
template <typename T>
bool is_in(T const& val, initializer_list<T> const& liste)
两个参数都会参与template argument deduction,并且各自推导出的类型不同。 T
从第一个参数中扣除为 std::string
,从第二个参数中扣除为 char const *
(在模板参数推导期间不考虑用户定义的转换,因此从 char const *
到std::string
没有出现),这会导致编译错误。
有多种方法可以解决此问题。一种是在传递给 is_in
的 braced-init-list 中构造 string
s,如果可以使用 C++,可以非常简洁地完成14 个 std::string_literals
.
using namespace std::string_literals;
if (is_in(test, {"foo"s, "bar"s}))
// ^ ^
另一种方法是构造一个 initializer_list<string>
并将其传递给 is_in
if (is_in(test, initializer_list<string>{"foo", "bar"}))
你也可以重新定义is_in
,使两种参数类型的模板类型参数不同,毕竟你不关心它们是否相同,你只需要它们可以通过operator==
template <typename T, typename U>
bool is_in(T const& val, initializer_list<U> const& liste)
另一种选择是防止其中一个函数参数参与模板参数推导。
template<typename T>
struct identity
{ using type = T; };
template <typename T>
bool is_in(T const& val, initializer_list<typename identity<T>::type> const& liste)
第二个参数的T
之上是一个non-deduced context,所以只有第一个参数参与模板实参推导
最后,is_in
可以换成std::any_of
。
auto elems = {"foo", "bar"};
if (any_of(begin(elems), end(elems), [&](string const& s) { return s == test; }))
如果你想避免 lambda 表达式样板,你可以使用 boost::algorithm::any_of_equal
.
auto elems = {"foo", "bar"};
if (boost::algorithm::any_of_equal(elems, test))
在 C++ Most efficient way to compare a variable to multiple values? 之后,我正在尝试使用 initializer_list
作为参数构建一个模板函数。当我只使用字符串时就会出现问题。我有以下代码:
functions.hpp
template <typename T>
bool is_in(T const& val, initializer_list<T> const& liste)
{
for (const auto& i : liste)
if (val == i) return true;
return false;
};
main.cpp
#include "functions.hpp"
using namespace std;
int main()
{
string test("hello");
if (is_in(test, {"foo", "bar"}))
cout << "good" << endl;
else
cout << "bad" << endl;
return 0;
}
我收到以下错误:
main.cpp: In function ‘int main()’:
main.cpp:18:34: error: no matching function for call to ‘is_in(std::string&, <brace-enclosed initializer list>)’
main.cpp:18:34: note: candidate is:
In file included from personnage.hpp:11:0,
from main.cpp:1:
functions.hpp:16:6: note: template<class T> bool is_in(const T&, const std::initializer_list<_Tp>&)
functions.hpp:16:6: note: template argument deduction/substitution failed:
main.cpp:18:34: note: deduced conflicting types for parameter ‘_Tp’ (‘std::basic_string<char>’ and ‘const char*’)
我不明白的是:当我在 main
中使用 int
或 double
而不是 string
时,一切都非常顺利...一个快速而肮脏的修复是将 is_in
声明为仅字符串的函数,但它不是很令人满意。
有谁知道如何保持模板和使用字符串一样吗?
答案很简单:
编译器不知道是否应该使用字符串中的 const char*
或 std::string
。
如果你写
if (is_in(test, {std::string("foo"), std::string("bar")}))
它按预期工作。
这也是您的编译器给您的提示:
main.cpp:18:34: note: deduced conflicting types for parameter ‘_Tp’ (‘std::basic_string’ and ‘const char*’)
在你的函数模板中
template <typename T>
bool is_in(T const& val, initializer_list<T> const& liste)
两个参数都会参与template argument deduction,并且各自推导出的类型不同。 T
从第一个参数中扣除为 std::string
,从第二个参数中扣除为 char const *
(在模板参数推导期间不考虑用户定义的转换,因此从 char const *
到std::string
没有出现),这会导致编译错误。
有多种方法可以解决此问题。一种是在传递给 is_in
的 braced-init-list 中构造 string
s,如果可以使用 C++,可以非常简洁地完成14 个 std::string_literals
.
using namespace std::string_literals;
if (is_in(test, {"foo"s, "bar"s}))
// ^ ^
另一种方法是构造一个 initializer_list<string>
并将其传递给 is_in
if (is_in(test, initializer_list<string>{"foo", "bar"}))
你也可以重新定义is_in
,使两种参数类型的模板类型参数不同,毕竟你不关心它们是否相同,你只需要它们可以通过operator==
template <typename T, typename U>
bool is_in(T const& val, initializer_list<U> const& liste)
另一种选择是防止其中一个函数参数参与模板参数推导。
template<typename T>
struct identity
{ using type = T; };
template <typename T>
bool is_in(T const& val, initializer_list<typename identity<T>::type> const& liste)
第二个参数的T
之上是一个non-deduced context,所以只有第一个参数参与模板实参推导
最后,is_in
可以换成std::any_of
。
auto elems = {"foo", "bar"};
if (any_of(begin(elems), end(elems), [&](string const& s) { return s == test; }))
如果你想避免 lambda 表达式样板,你可以使用 boost::algorithm::any_of_equal
.
auto elems = {"foo", "bar"};
if (boost::algorithm::any_of_equal(elems, test))