'Why I can't pass string literals to const char* const& in this specialized function template

Why pass a string literal to const char* const& in a specialized function template is illegal, while to const char* is legal?

Here's the thing. There are two excercises about template specialization in C++ Primer:

Exercise 16.63: Define a function template to count the number of occurrences of a given value in a vector. Test your program by passing it a vector of doubles, a vector of ints, and a vector of strings.

Exercise 16.64: Write a specialized version of the template from the previous exercise to handle vector<const char*> and a program that uses this specialization.

The code below is my answer, the compile error appears when I pass the string literal to this specialized count function.

// Count the number of occurrences of a given value in a vector
template <typename T>
std::size_t count(const std::vector<T>& vec, const T& value)
{
    std::size_t i = 0;
    for (const auto& v : vec) {
        if (v == value)
            ++i;
    }
    return i;
}

// A specialized version where T = const char*
template <>
std::size_t count(const std::vector<const char*>& vec, const char* const& value)
{
    std::size_t i = 0;
    for (const auto& v : vec) {
        if (!strcmp(v, value))
            ++i;
    }
    return i;
}

int main()
{
    std::vector<const char*> sVec{ "cpp", "primer", "cpp", "fifth", "edition", "Cpp", "cpp" };
    
    // Error message: no instance of function template "count" matches the argument list, 
    // argument types are: (std::vector<const char *, std::allocator<const char *>>, const char [4])
    std::cout << count(sVec, "cpp") << std::endl;

    return 0;
}

Besides, it's perfectly ok to pass a string literal to const char* const& in a nontemplate function, which makes me confused.

void test(const char* const& str)
{
    std::cout << str << std::endl;
}

int main()
{
    test("cpp"); // Prints "cpp" as expected
    return 0;
}


Solution 1:[1]

An array and a pointer are different types.

It is because of default conversions, that you can pass an array as a pointer parameter.

A template is just that, a "template" for a function.

There is no concrete function to match the parameters that have been specified. So the compiler tries to generate a function from the parameters and can't make a match, so it fails.

But when you specify the template parameter, the compiler knows exactly which function you want, generates it, then tries to call the function using standard rules for calling functions, which allow it to make the conversion.

When you have a template function definition, the requirements for function arguments are stricter.

In this case it will try to match the array and generate the function from the template and Fail. So no function will be available to call.

You can specialize:

// A specialized version where T = const char[ArraySize]
template<std::size_t ArraySize>
std::size_t count(const std::vector<const char*>& vec, const char (&value)[ArraySize])
{
    return count<const char*>(vec, value);
}

-- Edit

Or specifically defining a template parameter function, which defines a concrete function to call.

Solution 2:[2]

Oh, I changed the code to std::cout << count<const char*>(sVec, "cpp") << std::endl; Then everything is fine. It seems like compiler treats the string literal "cpp" as a const char [4] rather than const char* const&, so I have to explicitly specify the template parameter. Wonder why that happened.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 IceFox99