有没有办法在函数模板参数推导中强制隐式转换?
Is there a way to force implicit conversion in function template argument deduction?
我有这个 count_
函数模板,它计算一个值在其类型的向量中出现的次数:
template <typename T>
std::size_t count_(std::vector<T> const& vt, T const& value)
{
std::cout << "count_(vector<T>, T const&)\n";
std::size_t n{};
for(auto const& e : vt)
if(e == value)
++n;
return n;
}
template <>
std::size_t count_(std::vector<char const*> const& vcp, char const * const& value)
{
std::cout << "count_(vector<char const*>, char const*const&)\n";
std::size_t n{};
for(auto const& e : vcp)
if( !strcmp(e, value))
++n;
return n;
}
int main()
{
std::vector<std::string> vs{"function", "C++", "template", "C++", "specialization", "partial", "C++", "full"};
std::cout << count_(vs, std::string("C++")) << '\n';
std::vector<double> vd{3.14, 5.2, 7.7, 3.14, 56.87, 3.14, 6.8798, 12.545};
std::cout << count_(vd, 3.14) << '\n';
std::cout << count_(std::vector{7, 24, 16, 7, 81, 7, 5, 7, 23, 10, 7, 15, 8}, 7) << '\n';
std::vector<char const*> vcp{"function", "C++", "template", "C++", "specialization", "partial", "C++", "full"};
std::cout << count_(vcp, static_cast<char const*>("C++")) << '\n';
std::cout << "\ndone!\n";
}
程序运行良好,但我总是需要将第二个参数显式转换或传递给作为第一个参数传递的 vector
的元素类型。
我知道发生这种情况是由于模板参数推导中的数量有限或允许隐式转换。如果 count_
是一个普通的非模板函数,那么它可以正常工作,而无需将文字字符串转换为指向 char 的常量指针。
那么有没有更好的方法来避免这种转换std::string("C++")
、static_cast<char const*>("C++")
...?
如果可以的话,我想应该有类似“元编程”的东西,请您详细说明一个例子好吗?
最简单的解决方案是使用转发引用:
template <typename T, typename Value>
std::size_t count_(std::vector<T> const& vt, Value &&value)
{
std::cout << "count_(vector<T>, T const&)\n";
std::size_t n{};
for(auto const& e : vt)
if(e == value)
++n;
return n;
}
有了这个,count_(vs, "C++")
就可以编译了。
请注意,专业化需要相应调整:
template <typename Value>
std::size_t count_(std::vector<char const*> const& vcp, Value && value)
// ...
您需要记住,此模板现在将在比以前更多的情况下参与重载决议(由于转发引用)。如果这是不可取的(如果有 count_
的其他重载),则需要一些额外的工作来使此模板在无法编译时从重载解析中排除。
命令从向量中推导出模板参数,而不是从值中推导出
template<typename T> // v only required change v
typename std::vector<T>::size_type count_(std::vector<T> const &vt, typename std::vector<T>::value_type const &value) {
std::cout << "count_(vector<T> const&, T const&)\n";
typename std::vector<T>::size_type n = 0;
for(auto const &e : vt) if(e == value) n++;
return n;
}
模板参数推导基本上无法通过类型别名“看到”,尤其是那些属于未知专业化成员的别名。 count_
的第二个参数不再参与推导 T
。相反,vector
总是决定 T
,然后第二个参数被隐式转换为其类型。您的专业仍然是专业。实际上,它并没有改变。
// just cosmetic changes
template<>
std::vector<char const*>::size_type count_(std::vector<char const*> const &vcp, char const *const &value) {
std::cout << "count_(vector<char const*> const&, char const *const&)\n";
std::vector<char const*>::size_type n = 0;
for(auto const &e : vcp) if(!std::strcmp(e, value)) n++;
return n;
}
注意:在这种情况下,我们很“幸运”std::vector<T>::value_type
是 T
的方便可用的别名。通常,您可以使用 std::type_identity_t<T>
作为模板推导阻止程序,但这仅在 C++20 中可用。 (当然你也可以自己实现,两行就行!)
我有这个 count_
函数模板,它计算一个值在其类型的向量中出现的次数:
template <typename T>
std::size_t count_(std::vector<T> const& vt, T const& value)
{
std::cout << "count_(vector<T>, T const&)\n";
std::size_t n{};
for(auto const& e : vt)
if(e == value)
++n;
return n;
}
template <>
std::size_t count_(std::vector<char const*> const& vcp, char const * const& value)
{
std::cout << "count_(vector<char const*>, char const*const&)\n";
std::size_t n{};
for(auto const& e : vcp)
if( !strcmp(e, value))
++n;
return n;
}
int main()
{
std::vector<std::string> vs{"function", "C++", "template", "C++", "specialization", "partial", "C++", "full"};
std::cout << count_(vs, std::string("C++")) << '\n';
std::vector<double> vd{3.14, 5.2, 7.7, 3.14, 56.87, 3.14, 6.8798, 12.545};
std::cout << count_(vd, 3.14) << '\n';
std::cout << count_(std::vector{7, 24, 16, 7, 81, 7, 5, 7, 23, 10, 7, 15, 8}, 7) << '\n';
std::vector<char const*> vcp{"function", "C++", "template", "C++", "specialization", "partial", "C++", "full"};
std::cout << count_(vcp, static_cast<char const*>("C++")) << '\n';
std::cout << "\ndone!\n";
}
程序运行良好,但我总是需要将第二个参数显式转换或传递给作为第一个参数传递的
vector
的元素类型。我知道发生这种情况是由于模板参数推导中的数量有限或允许隐式转换。如果
count_
是一个普通的非模板函数,那么它可以正常工作,而无需将文字字符串转换为指向 char 的常量指针。
那么有没有更好的方法来避免这种转换std::string("C++")
、static_cast<char const*>("C++")
...?
如果可以的话,我想应该有类似“元编程”的东西,请您详细说明一个例子好吗?
最简单的解决方案是使用转发引用:
template <typename T, typename Value>
std::size_t count_(std::vector<T> const& vt, Value &&value)
{
std::cout << "count_(vector<T>, T const&)\n";
std::size_t n{};
for(auto const& e : vt)
if(e == value)
++n;
return n;
}
有了这个,count_(vs, "C++")
就可以编译了。
请注意,专业化需要相应调整:
template <typename Value>
std::size_t count_(std::vector<char const*> const& vcp, Value && value)
// ...
您需要记住,此模板现在将在比以前更多的情况下参与重载决议(由于转发引用)。如果这是不可取的(如果有 count_
的其他重载),则需要一些额外的工作来使此模板在无法编译时从重载解析中排除。
命令从向量中推导出模板参数,而不是从值中推导出
template<typename T> // v only required change v
typename std::vector<T>::size_type count_(std::vector<T> const &vt, typename std::vector<T>::value_type const &value) {
std::cout << "count_(vector<T> const&, T const&)\n";
typename std::vector<T>::size_type n = 0;
for(auto const &e : vt) if(e == value) n++;
return n;
}
模板参数推导基本上无法通过类型别名“看到”,尤其是那些属于未知专业化成员的别名。 count_
的第二个参数不再参与推导 T
。相反,vector
总是决定 T
,然后第二个参数被隐式转换为其类型。您的专业仍然是专业。实际上,它并没有改变。
// just cosmetic changes
template<>
std::vector<char const*>::size_type count_(std::vector<char const*> const &vcp, char const *const &value) {
std::cout << "count_(vector<char const*> const&, char const *const&)\n";
std::vector<char const*>::size_type n = 0;
for(auto const &e : vcp) if(!std::strcmp(e, value)) n++;
return n;
}
注意:在这种情况下,我们很“幸运”std::vector<T>::value_type
是 T
的方便可用的别名。通常,您可以使用 std::type_identity_t<T>
作为模板推导阻止程序,但这仅在 C++20 中可用。 (当然你也可以自己实现,两行就行!)