Function template recognizing quoted text as const char* instead of an std::string

I have this (dumbed down) function template inside a namespace M of my own:

On M.h
1
2
template <typename var>
void print(int, int, var);


On M.cpp (compiling and linking)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using std::string;

template <typename var>
void M::print(int x, int y, var Text){

    /*
        Function definition
    */


}
template void M::print<string>(int,int,   string);
template void M::print< int  >(int,int,   int   );
template void M::print< char >(int,int,   char  );




The point of it is being able to call for the function print() with either a string, an integer, or a character (and possibly other variables in the future).

This works great. However, when I call for the function expecting it to take a string:
M::print(23,7, "hello");

The compiler takes the text inside quotes as a const char*, and not an object of type std::string, giving an error. Now, there are two obvious solutions. One; adding (string) behind every quote whenever I want to print a string (Or using a macro such like #define S (string) . Two; replacing the template specialization from string to const char*.

I don't like either solution. The first is ugly and tears down the cleanliness of using templates, and the second defeats the purpose of using std::string's in C++. is there a way to make it take my std:string objects by default? Or which solution would you recommend?

I don't want to use function overloading as it makes me copy and paste the function three times over, and I'm already overloading the template, having a copy of the function that takes an object coord of my own as an argument instead of the two int's . That'll be making it a total of 6 copies of the almost same function.
Last edited on
You could define a non-template overload of print() that takes a const char * as its third parameter and then calls this->print(x, y, (std::string)Text).
If you move the definition to the header, the compiler can instantiate the template for you, and that will get rid of the linker error.
> Now, there are two obvious solutions.

As I see it, there are three obvious solutions.

1. Use string literal suffix for literal strings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>

template < typename T > void print( const T& v ) { std::cout << v << '\n' ; }

void print( const char* ) = delete ;

int main()
{
    using namespace std::literals ;

    print( "abcd"s ) ; // print( const std::string& )

    // print( "abcd" ) ; // error: deleted function 'void print(const char*)'
}

http://coliru.stacked-crooked.com/a/6da3a13fb970266a


2. Add a forwarding wrapper:
1
2
template <typename var> void M::print(int x, int y, var Text) { /* ... */ }
void M::print( int x, int y, const char* Text ) { M::print( x, y, std::string(Text) ) ; }


3. Add an explicit instantiation void M::print< const char* >( int, int, const char* );
1
2
3
4
5
6
template <typename var> void M::print(int x, int y, var Text) { /* ... */ }

template void M::print<string>(int,int,   string);
template void M::print< int  >(int,int,   int   );
template void M::print< char >(int,int,   char  );
void M::print< const char* >( int, int, const char* );
Well, it's ambiguous. You have to explicitly tell C++ what type your passing. You can also call print like so: print<std::string>(0,0," "); Not sure what the practical difference is. Might be two ways to doing the same thing.
Last edited on
@JLBorges and @helios:
wrapping appears to be the cleaniest answer, and I think it's a fantastic example of how people look at problems from different angles. Thank you!
My objective has been to just make the function templates and overloads and simply forget about it; not think about which specific instance I'm calling. Using a coordinate object or two integers depending on the occasion's convenience, or a string or ASCII character or numerical value when so needed, then just move on with whatever I want to do in my program(s).

Last edited on
A minor point, but I'd define print(int, int, const char *) and then do
1
2
3
print(int a, int b, const string &s) {
    print(a, b, s.c_str());
}

In other words, define printing a string in terms of printing a c_string, not the other way around. That way you can avoid creating a std::string when you want to print a c string.

Topic archived. No new replies allowed.