Template Constructor in a Class Template - How to Explicitly Specify Template Argument for the 2Nd Parameter

Template constructor in a class template - how to explicitly specify template argument for the 2nd parameter?

Fixing your code, the following would work:

template<class T> class TestTemplate {
public:
//constructor 1
template<class Y> TestTemplate(Y * p) {
cout << "c1" << endl;
}

//constructor 2
template<class Y, class D> TestTemplate(Y * p, D d) {
cout << "c2" << endl;
}

template<class A, class B>
void foo(A a, B b) {
cout << "foo" << endl;
}
};

int main() {
TestTemplate<int> tp(new int());

tp.foo<int*, string>(new int(), "hello");

TestTemplate<int> tp2(new int(),2);
}

You cannot use T for the class template parameter and the constructor template parameter. But, to answer your question, from [14.5.2p5]:

Because the explicit template argument list follows the function
template name, and because conversion member function templates and
constructor member function templates are called without using a
function name, there is no way to provide an explicit template
argument list for these function templates.

Therefore, you cannot explicitly specify template arguments for constructor.

Constructor with extra template argument

The correct syntax for the out-of-class definition of a template method in a template class is as follows:

template <typename A> // Class template parameters
template <typename B> // Method template parameters
Class<A>::Class() {
// ^^^
// Class template parameters

// ...
}

live example on wandbox

Why does an optional argument in a template constructor for enable_if help the compiler to deduce the template parameter?

Template argument deduction does not work this way.

Suppose you have a template and a function using a type alias of that template:

template <typename T>
struct foo;

template <typename S>
void bar(foo<S>::type x) {}

When you call the function, eg foo(1) then the compiler will not try all instantiations of foo to see if any has a type that matches the type of 1. And it cannot do that because foo::type is not necessarily unambiguous. It could be that different instantiations have the same foo<T>::type:

template <>
struct foo<int> { using type = int; };

template <>
struct foo<double> { using type = int; };

Instead of even attempting this route and potentially resulting in ambiguity, foo<S>::type x is a nondeduced context. For details see What is a nondeduced context?.

Specialize template class constructor on templated template parameter

A C++11 solution could be based on SFINAE: enabling the first or the second constructor if U is a S based type or not.

To make this, can be useful develop a type traits to detect if a type is (or isn't) S based; by example

template <typename>
struct isS : public std::false_type
{ };

template <typename T>
struct isS<S<T>> : public std::true_type
{ };

With isS, you can write your constructors (in the body of the A class) as follows

template <typename V = U>
A(T & t, bool b,
typename std::enable_if<false == isS<V>::value>::type * = nullptr )
: U(t, b)
{ std::cout << "generic A constructor" << std::endl; }

template <typename V = U>
A(T & t, bool b,
typename std::enable_if<true == isS<V>::value>::type * = nullptr)
: U(t, b, false)
{ std::cout << "S specific A constructor" << std::endl; }

If you need the template argument of S, you can define the specialization of isS as follows

template <typename T>
struct isS<S<T>> : public std::true_type
{ using type = T; };

and use it as typename isS<V>::type.

A full working example

#include <vector>
#include <iostream>
#include <type_traits>

template <typename T>
struct S
{
S (T const &, bool, bool)
{ std::cout << "S constructor" << std::endl; }
};

template <typename>
struct isS : public std::false_type
{ };

template <typename T>
struct isS<S<T>> : public std::true_type
{ };

template <typename T, typename U>
struct A : public U
{
template <typename V = U>
A(T & t, bool b,
typename std::enable_if<false == isS<V>::value>::type * = nullptr )
: U(t, b)
{ std::cout << "generic A constructor" << std::endl; }

template <typename V = U>
A(T & t, bool b,
typename std::enable_if<true == isS<V>::value>::type * = nullptr)
: U(t, b, false)
{ std::cout << "S specific A constructor" << std::endl; }
};

int main ()
{
long l { 0L };

// print "generic A constructor"
A<long, std::vector<int>> alv(l, true);

// print "S constructor>" and "S specific A constructor"
A<long, S<int>> als(l, true);
}

Explicitly specify template template types

The usual way to do decomposition like this is via partial specialization, which requires a helper class template:

namespace detail {
template<class> struct create; // undefined
template<template<class T> class C,class T>
struct create<C<T>> {
static C<T> make() {/* … */}
};
}

template<class T>
T createObject() {return detail::create<T>::make();}

The primary template can be defined if you want to support the general case, and other specializations may be added for other kinds of templates like std::array.



Related Topics



Leave a reply



Submit