3

I am new to templates in C++. Can anyone explain why my specialised constructor never gets executed. It works when I remove the const and reference operator.

#include<iostream>
#include<string>

using namespace std;

template<typename T>
class CData
{
public:
    CData(const T&);
    CData(const char*&);
private:
    T m_Data;
};

template<typename T>
CData<T>::CData(const T& Val)
{
    cout << "Template" << endl;
    m_Data = Val;
}
template<>
CData<char*>::CData(const char* &Str)
{
    cout << "Char*" << endl;
    m_Data = new char[strlen(Str) + 1];
    strcpy(m_Data, Str);
}

void main()
{
    CData<int> obj1(10);
    CData<char*> obj2("Hello");
}

The output is

Template

Template

Carlo Wood
  • 5,648
  • 2
  • 35
  • 47
Nidhin MS
  • 229
  • 1
  • 11
  • 1
    `main` must return `int`, not `void`. Also, the header for `strcpy` and `strlen` is ``, not ``. – dyp Aug 27 '14 at 13:50
  • 2
    A string literal is an array lvalue, which can be converted to a pointer prvalue. A pointer prvalue cannot bind to a non-const lvalue reference like `const char* &`. That's why the first ctor is called. – dyp Aug 27 '14 at 13:52
  • 1
    By the way, this isn't a partial specialization. It is an explicit specialization of a single member function. – dyp Aug 27 '14 at 13:56
  • Note that the selection of the first ctor in the second call uses a deprecated conversion: `const T&` for `T == char*` yields a `char* const&`, which requires converting the *array of 6 `const char`* of the string literal to a `char*` (non-const). – dyp Aug 27 '14 at 14:04

2 Answers2

5

Because you cannot bind "Hello" to a const char*&.

The information dyp added in comments is quite interesting:

A string literal is an array lvalue, which can be converted to a pointer prvalue. A pointer prvalue cannot bind to a non-const lvalue reference like const char* &

Which means you can actually make it work by replacing const char*& by const char* const&, or even const char* && in c++11, not sure if this is really smart in your use case though.

Community
  • 1
  • 1
Drax
  • 12,682
  • 7
  • 45
  • 85
  • *"Which means you can actually make it work"* Or you just use `const char*` without any reference. I'm not sure what the OP intends to do, though. – dyp Aug 27 '14 at 14:05
  • @Drax to match template declaration you should use `char* const&`. – Anton Savin Aug 27 '14 at 14:27
2

UPDATE I got everything wrong, rewrote the answer completely.

First, this constructor

template<>
CData<char*>::CData(const char* &Str)

is not a specialization of CData(const T&), because the Str parameter here is a non-const reference to pointer to const char. So it's a definition of non-templated constructor CData(const char*&).

Second, "Hello" has type "array of n const char" (see What is the type of string literals in C and C++?) so it can't be converted to non-const reference. This is why "Template" constructor is called.

The correct specialization is

template<>
CData<char*>::CData(char* const& Str)

That is, it accepts a const reference to char*.

And after that you should remove CData(const char*&), unless you need for example this code to compile:

const char* foo = "foo";
CData<int> obj2(foo);

So here is the code:

template<typename T>
class CData
{
public:
    CData(const T&);
private:
    T m_Data;
};

template<typename T>
CData<T>::CData(const T& Val)
{
    ....
}

template<>
CData<char*>::CData(char* const& Str)
{
    ....
}

// warning: deprecated conversion from string constant to 'char*'
CData<char*> obj2("Hello"); // calls CData(char* const&)

The proper way of fixing above warning is to add another specialization:

template<>
CData<const char*>::CData(const char* const& Str)
{
    ...
}

CData<const char*> obj2("Hello"); // calls CData(const char* const&)
Community
  • 1
  • 1
Anton Savin
  • 40,838
  • 8
  • 54
  • 90