-
Very smart compiler?
Ok, this is going to be a bit hard to explain.
I have two classes. Ones called CString and another is called CBuffer.
CString has 5 constructors:
Code:
CString(const CString& pString);
CString(const char* pString = "");
CString(int pInteger);
CString(float pFloat);
CString(double pDouble);
The first is a copy constructor, the second (default) is for copying a c-string into
the string class and the last 3 convert numbers to a c-string than copy them
into the string class.
The CBuffer class has 4 constructors:
Code:
//Constructors
CBuffer();
CBuffer(const CBuffer& pBuffer);
CBuffer(const CString& pString);
//Template constructor
template <class T> CBuffer(const T& pData);
The first is a default constructor, the second is a copy constructor, the third is for
copying a CString class and the fourth is a template constructor to copy simple data types
such as int, float, doubles, structures, etc.
CBuffer also has this:
Code:
CBuffer& operator=(const CBuffer& pData);
Now i want to know how come when i do this:
Code:
CBuffer myBuff;
myBuff = "Hello";
It converts the const char* to a CString() (using the CString(const char*) constructor) than calls
the CBuffer(const CString& pString) constructor to convert it to a CBuffer() object, THAN
calls the "=" operator. WHICH IS WHAT I WANT.
BUT when i do this:
Code:
CBuffer myBuff;
myBuff = 1234;
It does NOT convert the integer into a CString() than convert the CString() into a CBuffer() than
pass the CBuffer() to the '=" operator. Instead it calls "template <class T> CBuffer(const T& pData);"
and than passes the resulting CBuffer() into the "=" operator. WHICH IS WHAT I WANT.
So although im not having any troubles, i just want to know how it knows to convert "const char*" to a
CString() object but not an integer even though they both have constructors in the CString() object.
The compiler is mingw.
Hmm let me simplify it. How come CBuffer(32) calls the template constructor, while CBuffer("32") calls the
CBuffer(const CString& pString); constructor even though CString() has a constructor for both of these datatypes?
-
I'm not quite sure, but I think it has to do with the way it resolves types...
What happens if you switch the order that your operator=() functions are declared?
-
Actually its got little to do with the operator=(). Not sure why i mentioned it. You can get the same effect without using it like this:
Code:
CBuffer myBuff(32);
CBuffer myBuff("32");
First one calls the template constructor, second one calls the "CBuffer(const CString& pString)" constructor.
The reason it calls the "CBuffer(const CString& pString)" is because CString() has a constructor for "const char*", but it also has a constructor for "int" so why does it do it differently.
-
My WAG (and I've been out of C++ land for about 5 years now -- wow, time flies -- so take this with a grain of salt):
The compiler is unwilling to make the assumption that 32 is an integer, as it could be unsigned or a long? What happens if you don't provide the template constructor? Does it not compile due to ambiguity? That's sort of what I'd expect.
-
It's because literal strings are treated as character arrays, so a literal string will prefer a template function over a const char *.
There are two possible solutions: cast the string literal to a char * or write a template specialization for character arrays.
The specialization will look something like this:
Code:
template <int I> CBuffer<char[I]>(char pString[I]);
PS. Why do my I's get converted to lowercase?
-
Could you perhaps provide a compilable example and mention what compiler are you using?
I ran this snippet and for me it calls the template constructor in both cases.
Code:
#include <iostream>
using std::cout;
class CString
{
public:
CString(unsigned ) { cout << "CString(unsigned)\n"; }
CString(const char*) { cout << "CString(const char*)\n";}
};
class CBuffer
{
public:
CBuffer(const CString&) { cout << "CBuffer(const CString&)\n";}
template <class T>
CBuffer(const T&)
{
cout << "CBuffer(const T&)\n";
}
};
int main()
{
CBuffer a("hello world");
CBuffer b(10u);
}
I thought the rule with templates was:
1) If there is an exactly matching non-template version, use this
2) else use the templated version (considering more specialized overloads first).
In above code there is no exact match, so templated constructor is used. Even though there is a constructor for CString and CString has constructors for both const char* and unsigned I can't see how this could be considered an exact match and used over the templated version (although it gets used if the templated constructor is commented out).
As to literal strings not matching to const char*, if that was so then both my MingW and Comeau online would have to be wrong, because they pick foo(const char*):
Code:
#include <iostream>
#include <string>
using std::cout;
void foo(const char* p)
{
cout << "foo(const char*)\n" << p << "\n\n";
}
void foo(const std::string& s)
{
cout << "foo(const string&)\n" << s << "\n\n";
}
template <class T>
void foo(const T& t)
{
cout << "template\n" << t << "\n\n";
//this would create a compile error with Comeau online
//if this overload was used
//int n = t * 2;
}
int main()
{
foo("Hello world");
}
(If foo(const char*) is commented out, template overload is preferred to std::string version - because that is not an exact match.)