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

Time:02-11

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;
}

CodePudding user response:

You don't "pass to a function template specialization". The call is deduced against the original template definition (regardless of any specialization), deducing a type if successful. Then if a specialization exists for the deduced type then that will be called.

The error message is about type deduction failing . You should see the exact same error even if you delete the specialization.

The type deduction fails because T occurs in two different parameters but the result of deduction differs for each parameter:

  • Deducing vector<T>& vec against vector<const char *> produces T=const char *
  • Deducing const T& value against "cpp" produces T=const char[4] .

Deduction only succeeds if all instances of T being deduced produce the same type. There is no extra step of different types being reconciled by considering available conversions.

One way to solve the problem would be to use overloading instead of specialization (i.e. delete the text template <>). Then there is an overload set consisting of the non-template function, and the result of deduction (if any). If deduction fails , as it does, it is not an error because the overload set still contains something.

CodePudding user response:

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.

CodePudding user response:

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.

  • Related