0

I have a template function void foo(const T&). I need specialized implementations for [T = int] and [T = const char*].

#include <iostream>

template<class T>
void foo(const T& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
}

template<>
void foo(const int& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
}

int main(int argc, char *argv[])
{
    int i = 42;
    const char *bar = "xyz";
    foo(i);
    foo(bar);
    return 0;
}

The specialization for [T = int] works, the output is:

void foo(const T&) [with T = int]
42
void foo(const T&) [with T = const char*]
xyz

But, it won't compile if I try to specialize for [T = const char*] like so:

template<>
void foo(const char*& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
}

17:6: error: template-id 'foo<>' for 'void foo(const char*&)' does not match any template declaration

What has me puzzled is that it appears to correctly deduce [T = const char*] when no specialization is present, but complains if I try to implement one.

Any ideas where I'm going wrong?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 2
    What if you place the template specialization parameters where they belong? ([example](https://godbolt.org/z/r4jrPjoWa)) – Ted Lyngmo Jul 25 '22 at 20:38
  • 2
    FWIW this can be easily solved by turning those specializations into overloads. Here is a great video that gets into some of the pitfalls of using specialization: https://www.youtube.com/watch?v=NIDEjY5ywqU – NathanOliver Jul 25 '22 at 20:38
  • 4
    In `const char *p`, `p` itself isn't const. If I'm reading this correctly, your template requires a reference to a const thing, not to a non-const thing that points to a const thing. – Mat Jul 25 '22 at 20:39
  • 1
    Say it with me: "leading const is misleading!" – StoryTeller - Unslander Monica Jul 25 '22 at 20:44

2 Answers2

6

You need a second const

template<>
void foo(const char* const& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
}

The template requires a const reference, but const char*& is a reference to a pointer to a const char.

template<class T>
void foo(const T& arg)

Even better would be to use overloads instead of function template specialization, e.g.

template<class T>
void foo(const T& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
}

void foo(const int& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
}


void foo(const char*& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
}
Jens
  • 9,058
  • 2
  • 26
  • 43
0

In this function template declaration

template<class T>
void foo(const T& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
}

you have a constant reference to the type T. When the function is called like

const char *bar = "xyz";
foo(bar);

then the deduced type T is const char * (the type of a pointer to a string literal).

You may imagine it the following way

typedef const char *T;

So the constant reference to the type T will look like

const char * const &

Here is a demonstration program.

#include <iostream>
#include <type_traits>

template<class T>
void foo(const T& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
    std::cout << std::boolalpha
        << std::is_same_v<T, const char *> << '\n';
    std::cout << std::boolalpha
        << std::is_same_v<decltype( arg ),  const T&> << '\n';
    std::cout << std::boolalpha
        << std::is_same_v<decltype( arg ),  const char * const &> << '\n';
}

int main(int argc, char *argv[])
{
    const char *bar = "xyz";
    foo(bar);
    return 0;
}

The program output is

void foo(const T &) [T = const char *]
xyz
true
true
true

Compare this program with the program below

#include <iostream>
#include <type_traits>

typedef const char *T;

void foo(const T& arg)
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    std::cout << arg << std::endl;
    std::cout << std::boolalpha
        << std::is_same_v<T, const char *> << '\n';
    std::cout << std::boolalpha
        << std::is_same_v<decltype( arg ),  const T&> << '\n';
    std::cout << std::boolalpha
        << std::is_same_v<decltype( arg ),  const char * const &> << '\n';
}

int main(int argc, char *argv[])
{
    const char *bar = "xyz";
    foo(bar);
    return 0;
}

The program output is the same as shown above except the first record

void foo(const T &)
xyz
true
true
true
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335