Reputation: 3164
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?
Upvotes: 2
Views: 664
Reputation: 29193
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!)
Upvotes: 5
Reputation: 118300
The simplest solution is to use a forwarding reference:
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;
}
And with this, count_(vs, "C++")
will compile.
Note that the specialization will need to be adjusted, accordingly:
template <typename Value>
std::size_t count_(std::vector<char const*> const& vcp, Value && value)
// ...
You'll need to keep in mind that this template will now participate in overload resolution in more situations than before (due to the forwarding reference). If that's undesirable (if there are other overloads of count_
) some additional work will be needed to have this template get excluded from overload resolution, when it won't compile.
Upvotes: 4