jkerian
jkerian

Reputation: 17036

Specialized template class (descended from general case) with a template constructor using a dependent type

I need to make a templated class constructor take a dependent type (on the templated class types). This works fine, unless I have a specialization of the templated class, in which case the constructor doesn't seem to be found. And if I reimplement the constructor in the specialized subclass, I don't seem to be able to initialize the base class either through the constructor or directly.

Is there any way to preserve this relatively narrow interface outside of the class?

class T1 {};
class T2 {};

// KeyType
template <typename SELECT>
class KeyType {
};

// Declarations
template <typename SELECT = T1>
class TestTemplate {
protected:
    TestTemplate() {}
    KeyType<SELECT> k;
public:
    TestTemplate(KeyType<SELECT> const &key) : k(key) {}
};

template <>
class TestTemplate<T2> : public TestTemplate<T1> {
};


int main() {

    KeyType<T2> key;
    TestTemplate<T2> foo(key);
    return 0;
}

After staring at it for a bit, I realize that the problem is that I can't just arbitrarily convert the KeyType<T2> to a KeyType<T1> for the TestTemplate<T1> baseclass.

g++ gives:

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'int main()':
main.cpp:27:29: error: no matching function for call to 'TestTemplate<T2>::TestTemplate(KeyType<T2>&)'
     TestTemplate<T2> foo(key);
                             ^
main.cpp:20:7: note: candidate: TestTemplate<T2>::TestTemplate()
 class TestTemplate<T2> : public TestTemplate<T1> {
       ^
main.cpp:20:7: note:   candidate expects 0 arguments, 1 provided
main.cpp:20:7: note: candidate: constexpr TestTemplate<T2>::TestTemplate(const TestTemplate<T2>&)
main.cpp:20:7: note:   no known conversion for argument 1 from 'KeyType<T2>' to 'const TestTemplate<T2>&'
main.cpp:20:7: note: candidate: constexpr TestTemplate<T2>::TestTemplate(TestTemplate<T2>&&)
main.cpp:20:7: note:   no known conversion for argument 1 from 'KeyType<T2>' to 'TestTemplate<T2>&&'

Upvotes: 0

Views: 68

Answers (1)

Kristian Duske
Kristian Duske

Reputation: 1779

Constructors are not inherited at all in C++, so your instance TestTemplate<T2> only has the implicitly declared constructors (default, copy and move it seems by the error messages you posted). In case you assume that, when you specialize a template, the specialized template inherits declarations and definitions from the template that is being specialized: that is not the case. You have to redeclare and redefine all members in your specialized template again.

So in your case, you have to add the appropriate constructor to your specialized template like so:

template <>
class TestTemplate<T2> : public TestTemplate<T1> {
public:
    TestTemplate<KeyType<T2> const &key) :
    TestTemplate<T1>(...) {}
};

Since the base class, TestTemplate<T1>, does not provide a default constructor, you have to call that constructor, however, it is unclear what you want to pass to it as the key argument, since you have a reference to an instance of KeyType<T2> and not KeyType<T1>.

If you do wish to inherit the constructor from TestTemplate<T1>, you can do this with a using directive, assuming you're using C++11:

template <>
class TestTemplate<T2> : public TestTemplate<T1> {
public:
    using TestTemplate<T1>::TestTemplate;
};

But admittedly, I'm not 100% about the syntax here.

Upvotes: 1

Related Questions