c++ - 为什么带有initializer_list参数的模板对字符串的行为不正确?

标签 c++ templates c++11 initializer-list template-argument-deduction

已关注 C++ Most efficient way to compare a variable to multiple values? ,我正在尝试构建一个以 initializer_list 作为参数的模板函数。当我仅使用字符串时就会出现问题。我有以下代码:

函数.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 中使用 intdouble 而不是 string 时code>,一切都很顺利...一个快速但肮脏的修复是将 is_in 声明为仅字符串函数,但它不是很令人满意。

有谁知道如何保持模板和使用字符串相同?

最佳答案

在您的函数模板中

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 的隐式转换不会出现),这会导致编译错误。

有多种方法可以解决此问题。一种是构建 string您传递给 is_inbraced-init-list 中的 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++ - 为什么带有initializer_list参数的模板对字符串的行为不正确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33200019/

相关文章:

c++ - GCC C++14/17 成员函数指针模板参数的区别

c++ - 为什么这个特征类不能工作来测试一个类是否具有特定的 typedef?

c++ - 为什么 C++ 模板编译器不能从协变智能指针参数推断类型?

c++ - Stroustrup 的 Can_Copy 模板如何工作?

c++ - 基于模板参数的可选范围检查

c++ - 为什么声明 `void(*pf)(int) = bar;` 会触发下面代码片段中的 `static_assert`?

c++ - 条件反序列化

c++ - 加载位图文件 (.bmp)

c++ - 在调用传递给模板函数的函数时调用指向成员函数的指针

C++11 和 if(整数)