I have this count_
function template that counts the occurrences of a value in a vector of that its type:
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";
}
The program works just fine however I always need to explicitly cast or pass the second argument to the type of the element type of the vector
that is passed as the first argument.
I know this occurs due to the limited number or allowed implicit conversion in template Argument Deduction. And if count_
is an ordinary non-template function then it works OK without casting a literal string to a constant pointer to char.
So is there a better way to avoid such casts std::string("C++")
, static_cast<char const*>("C++")
...?
I guess there is something like "meta-programming" if so could you elaborate an example please?
Command the template argument to be deduced from the vector, but not from the value
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;
}
Template parameter deduction basically cannot "see" through type aliases, especially not ones that are members of an as-yet unknown specialization. The second argument to count_
no longer participates in deducing T
. Rather, the vector
always decides T
, and then the second argument is implicitly converted to its type. Your specialization remains a specialization. Actually, it doesn't change.
// 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;
}
Note: in this case we're "lucky" that std::vector<T>::value_type
is a conveniently available alias for T
. In general, you can use std::type_identity_t<T>
as a template deduction blocker, but that's only available in C++20. (Of course, you can implement it yourself—it's only two lines!)