2

I am trying to find a way to deal with some legacy code. There is a templated class which I would like to specialize the constructor to pass different arguments to its base when instantiated with a certain parameter.

template<typename T, typename U>
class A : public U {
public:
    A(T &t, bool b);
    // Other member functions
}

template<typename T, typename U>
A<T, U>::A(T &t, bool b) 
    : U(t, b) {}

I need to change thebehavior of this constructor when U is of a certain (templated) class.

template<typename Z>
class S;

template<typename T>
template<typename Z>
A<T, S<Z>>::A(T &t, bool b)
    : S<Z>(t, b, false) {}

Is this possible? I know that class template specializations cannot be done without redefining a new class. But I would rather only specialize this behavior, and not any other member functions of this class U.

max66
  • 65,235
  • 10
  • 71
  • 111
shane
  • 1,742
  • 2
  • 19
  • 36

3 Answers3

0

You can add a function called id to each of your classes, with each class' id function returning a different value. You can then what was returned by a type's id function when it is passed in.

Cpp plus 1
  • 990
  • 8
  • 25
0

If you don't want to specialize this class, you can specialize one you'd inherit:

template<typename T, typename U>
class A_impl : public U {
public:
    A_impl(T &t, bool b) : U(t, b) { }
};

template<typename T, typename Z>
class A_impl<T,S<Z> > : public S<Z> {
public:
    A_impl(T &t, bool b) : S<Z>(t, b, false) { }
};

template<typename T, typename U>
class A : public A_impl<T,U> {
public:
    using A_impl<T,U>::A_impl; // C++11 : inherit A_impl's constructor here
    A(T &t, bool b) : A_impl<T,U>(t, b) {} // or C++98 calling it
};
O'Neil
  • 3,790
  • 4
  • 16
  • 30
0

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);
 }
max66
  • 65,235
  • 10
  • 71
  • 111