Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why a template specialization cannot change the return type?

Tags:

c++

templates

After reading this question I had to realize once more how little I know about templates. I can understand, that a template specialization like this

// A
template <typename T> void foo(T x){}
template <> void foo<double>(int x){}

cannot work (error: template-id 'foo<double>' for 'void foo(int)' does not match any template declaration). Not only would it make little sense, but also parameter deduction would have no chance to get the right T. However, I do not understand why it does not work for return types:

// B
template <typename T> int foo(T x){}  
template <> double foo<double>(double x){}

(similar error as above). Actually I dont have any particular use case at hand, but still I would be interested in how to choose a return type depending on T. As a workaround I found this:

// C 
template <typename T> struct foo { static int moo(T x){return x;} };
template <> struct foo<double> { static double moo(double x){return x;} };

So it is possible to choose the return type depening on T. However, I am still puzzled...

What is the reason for B being not possible?

like image 709
463035818_is_not_a_number Avatar asked Mar 11 '23 12:03

463035818_is_not_a_number


2 Answers

Even if strange, you may have

template <typename T>
void foo(int);

template <typename T>
char foo(int);

Demo

So your specialization would be ambiguous.

like image 179
Jarod42 Avatar answered Apr 27 '23 21:04

Jarod42


Actually, you can work around it either by using a template parameter for the return type or a traits class.
As an example:

#include<type_traits>

template<typename T>
T f() { return T{}; }

template<typename>
struct Traits;

template<>
struct Traits<int> {
    using ReturnType = char;
};

template<>
struct Traits<char> {
    using ReturnType = int;
};

template<typename T>
typename Traits<T>::ReturnType g() { return T{}; }

int main() {
    static_assert(std::is_same<decltype(f<int>()), int>::value, "!");
    static_assert(std::is_same<decltype(f<double>()), double>::value, "!");
    static_assert(std::is_same<decltype(g<int>()), char>::value, "!");
    static_assert(std::is_same<decltype(g<char>()), int>::value, "!");
}

In the case of g, if parameters are deduced you have that two apparently identical calls have different return types.

That said, specializations don't allow the user to change the declaration of a function.
That's why you have to play with such a definition to have different return types for different template arguments.

like image 45
skypjack Avatar answered Apr 27 '23 22:04

skypjack