Function Overloading and Template Specializations

Why is it not possible to do this?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename T>
T Func(const T& t)
{
  //do stuff
}
template<>
string Func<string>(const string& str)
{
  //do different stuff
}
template<>
string Func<string>(const string& str, unsigned long num)
{
  //do different stuff num times
}


This would have been useful in my code, but instead I have to change the second template specialization function overload into its own function. Why won't the template specializations work like normal functions and have on overload the other?
Last edited on
Why is it not possible to do this?


Why should it be possible?

You're not specializing a template there, you're defining an entirely different function by adding an additional parameter.

If you want to overload the function to give it another parameter, you can. But it won't be a specialization of the template.
The second and third aren't specializations.

Just make the second and third non-templates.
But then you can call Func<string> and it will work the wrong way. And what do you mean they aren't specializations?
Last edited on
Read this: http://www.gotw.ca/publications/mill17.htm

Yes, it will. On the other hand, explicitly specifying the template parameter is unnecessary in this case.

Well, I was simply trying to avoid turning my second template specialization into a whole new function. I am in a situation similar to this, but not exactly (it is a much simplified version):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

ifstream ifs ("test.txt", ios_base::in | ios_base::binary);

template<typename T>
T Read()
{
	T temp;
	ifs.read((char*)&temp, sizeof(T));
	return(temp);
}
template<>
string Read<string>() //Specialized for strings, null-terminated
{
	string temp;
	char c;
	while(ifs.read(&c, 1), c != 0)
	{
		temp += c;
	}
	return(temp);
}
template<>
string Read<string>(unsigned long length) //Specialized for strings, length-terminated
{//This function generates an error that this is not a specialization of a function template
	string temp;
	char c;
	for(; length > 0; --length)
	{
		ifs.read(&c, 1);
		temp += c;
	}
	return(temp);
}

int main()
{
	unsigned char Version = Read<unsigned char>();
	string FirstString = Read<string>(); //FirstString is null-terminated in the file
	string SecondString = Read<string>(8); //SecondString is not null terminated and has a fixed length of 8
}
It's not a real hassle to make another function, and I have done it, I am just curious as to the reasoning behind this.

I have read that article and I see how it pertains to my situation somewhat, but not entirely. I have considered the information, but I am confused as to how it relates to my code above...
Essentially it means that what you are trying to do on lines 26-27 is not a full specialization of the template on line 9 because specialization requires the same number of parameters to the function.

What I was trying to convince you to do is to turn your "specializations" into overloads and avoid the template. To your point, it does allow the user to shoot himself in the foot by using an explicit specialization of the function template version (in your original code). I could fix that with some SFINAE and boost::disable_if, however now that I see your new code, your new scenario is completely different.

The reason it is different is because C++ does not allow overloading on return type alone. This means that you have to specialize for std::string, which makes your third function (26-27) impossible. The best I can offer (which I think has already been suggested) is to add a parameter to all three functions -- a length -- default it to a value in all cases but the third.

Another option is to create a helper object -- say fixed_len_string -- which contains the length, and then change your third function to specialize for fixed_len_string with no parameters.

Something like:

1
2
3
4
5
6
7
8
9
struct fixed_len_string 
{
    typedef std::string::size_type size_type;

    fixed_len_string( size_type s ) : str( s, ' ' ) {}

  private:
    std::string str;
};


Obviously you need to create a more complete API for this object, and, if it is to enforce its invariant (namely, that str.length() never changes post construction), you won't simply be able to write simple delegator functions.
Thanks for the help, I wasn't aware of the same-parameter-count requirement, but I thought it was something like that. And sorry that my original scenario didn't quite match up with my actual scenario like I intended.

The fixed length string class idea is a good one, and I might use it if I need to. Thanks for the helpful explanation and suggestion, this is starting to more correctly define templates to me.

Alternatively, could I not just make a dummy fixed_length_string class that remembers the length, and have my specialization return a std::string? Never mind, I see too many problems with this.
Last edited on
Alternatively, could I not just make a dummy fixed_length_string class that remembers the length, and have my specialization return a std::string?


Sure, you can do that. The difference between that idea and the one I gave is that you lose the ability to enforce the invariant. I went for a more general solution, not knowing the full extent of how you plan to use the object. Perhaps you don't need to enforce the constraint outside of that function, in which case your idea is fine. Or, another approach (which does not enforce the invariant post construction) is:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct maybe_fixed_len_string
{
    explicit maybe_fixed_len_string( const std::string& initial ) : str( initial ) {}

    maybe_fixed_len_string( const std::string& initial, size_type maxchars ) : str( initial ) 
        { if( maxchars < initial.length() ) str.resize( maxchars ); }

    operator std::string&() { return str; }
    operator const std::string&() const { return str; }

  private:
    std::string str;
};


Last edited on
I bet you already know how I would do it :D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

ifstream ifs ("test.txt", ios_base::in | ios_base::binary);

template <class T>
struct Reader { static T Get() { /*...*/ } };

template<>
struct Reader<string>
{
    struct Nulled { static string Get() { /*...*/ } };

    template <unsigned N>
    struct Sized { static string Get() { /*...*/ } };
};

int main()
{
	unsigned char Version = Reader<unsigned char>::Get();
	string FirstString    = Reader<string>::Nulled::Get();
	string SecondString   = Reader<string>::Sized<8>::Get();
}

Though, this does not enforce the invariant post construction.
It's a nifty way to do it, but my read functions are part of a specially designed inward file stream class I made, so I'd have to be using subclasses and all that crazy but delicious stuff.
To your point of view, it allows users to shoot in the foot by using a template explicit specialization version of the function itself. I can solve some SFINAE and boost:: disable_if, but now I see your new code, the new situation is completely different....

<a href="http://www.kcq.com/Runescape.gold">Buy RS Gold</a>,<a href="http://www.tera2buy.com/">Tera Gold</a>

Topic archived. No new replies allowed.