Mutating Algorithm
Mutating Algorithm

Reputation: 2758

Function template specialization in C++, no Instance of overloaded function

I'm learning about function template specialization in C++ and am tasked with writing a template function called plus that returns the sum of it's two arguments which maybe of different types. One version that accepts by value and another by pointer. As an added challenge, I'm asked to overload this function so that it concatenates two strings.

template <typename T1, typename T2> decltype(auto) plus(const T1& a, const T2& b) {
    return a + b;
}

template <typename T1, typename T2> decltype(auto) plus(const T1* a, const T2* b) {
    return *a + *b;
}

// concatenate two strings
template <>
std::string_view plus<std::string_view, std::string_view> (std::string_view a, std::string_view b) {
    return std::string { a } + std::string{ b };
}

The problem is that I'm getting an error on the specialization overload of the function to concatenate two strings. The reason I decided to choose std::string_view over std::string is so that when calling the function with string literals (const char*) it wouldn't resolve to the second definition that accepts a const * which I'm guessing would be resolved over std::string.

So I can't really figure out what's going on. Just a wild guess but maybe this has something to do with me having two different template functions called plus and it can't figure out which one I'm trying to specialize / overload?

UPDATE:

The issue seems to be with template resolution. The definition that accepts const T* is always preferred for any string literals. Just trying to find a fix.

Upvotes: 0

Views: 265

Answers (2)

Ranoiaetep
Ranoiaetep

Reputation: 6637

If you have access to C++20, then this could be done pretty easily with concepts.

Anything that's convertible to string_view, such as string and const char*, will be taken to this function:

template<typename T>
concept StringView = std::convertible_to<T, std::string_view>;

auto plus(StringView auto a, StringView auto b) 
{
    return std::string(a).append(b);
}

Similarly, you can define other concepts easily and just exlucde StringView:

template<typename T>
concept Reference = std::is_reference_v<T> && !StringView<T>;

template<typename T>
concept Pointer = std::is_pointer_v<T> && !StringView<T>;

auto plus(const Reference auto a, const Reference auto b)
{
  ⋮
  ⋮

Upvotes: 1

0xd34dc0de
0xd34dc0de

Reputation: 523

This would be my suggestion:

template <typename T1, typename T2, typename T3> T3 plus(const T1& a, const T2& b) {
    return a + b;
}

template <typename T1, typename T2, typename T3> T3 plus(const T1* a, const T2* b) {
    return *a + *b;
}

template <typename T1, typename T2, typename T3> T3 plus(T1 a, T2 b) {
    return a + b;
}

// concatenate two strings
template <>
std::string plus<std::string_view, std::string_view> (std::string_view a, std::string_view b) {
    return std::string(a).append(b);
}

Since string views needs to refer to the content of another string you need to return a string since the newly created string_view would point to a temporary object.

Also there is no way to concatenate 2 string_view's together since concatenating two strings together would require that string_view's are able to hold references to other string views (since they don't hold string content themselves).

Furthermore, a third typename is required since this implementation would return another type (std::string) since you don't want to return a string_view of a temporary

Upvotes: 1

Related Questions