Reputation: 2758
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
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
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