lexical
lexical

Reputation: 76

Template class with template constructor specialization for initializing a templated base class

I have a template base class with a template parameter of type bool. The constructor parameter list of this base class depends on whether the template parameter is true or false. I want to derive from this class another template class with a template parameter that is any type. I need this derived class to call the correct constructor of that Base class depending on traits of that type.

The example below is not all-encompassing. Integral or not, the base class template bool could be for any type trait. Also, the type passed to the template parameter of the derived class could be any type.

I've tried several different methods for trying to syntax this thing out, but the closest I've gotten is with the code below. However it errors due to the fact that the derived class needs to be fully specialized.

#include <type_traits>
using namespace std;

template<bool IsIntegral> struct Base 
{
    template<bool IsI = IsIntegral,
        typename I = typename enable_if<IsI>::type>
    Base(int param) {}

    template<bool IsI = IsIntegral,
        typename I = typename enable_if<!IsI>::type>
    Base() {}
};

template<class T> class CL :
    Base<is_integral<T>::value>
{
public:
template<bool U = is_integral<T>::value> CL();

};

template<>
template<class T>
CL<T>::CL<true>() : // error: ISO C++ forbids declaration of ‘CL’ with no type [-fpermissive]
                    // CL<T>::CL<true>() :
                    //                 ^
                    // error: non-type partial specialization ‘CL<true>’ is not allowed
                    // error: no declaration matches ‘int CL<T>::CL()’
                    // CL<T>::CL<true>() :
                    // ^~~~~
    Base(4) { }

template<>
template<class T>
CL<T>::CL<false>()  // error: ISO C++ forbids declaration of ‘CL’ with no type [-fpermissive]
                    // CL<T>::CL<true>() :
                    //                 ^
                    // error: non-type partial specialization ‘CL<true>’ is not allowed
                    // error: no declaration matches ‘int CL<T>::CL()’
                    // CL<T>::CL<true>() :
                    // ^~~~~
    { }

int main () {
    // Base<true> a; // won't compile as expected
    Base<true> a(4);
    // Base<false> b(4); // won't compile as expected
    Base<false> b;
    
    CL<int> c; // integral
    CL<int*> d; // non-integral
    // should work for any types other than int and int*

    return 0;
}

I need to keep the type T generic and can't fully specialize the class CL. What is the correct syntax for this? Is there a workaround?

I'm using gcc-g++ version 8.3.0-6.

Thanks in advance!

Upvotes: 3

Views: 824

Answers (3)

dfrib
dfrib

Reputation: 73186

C++20 and requires clauses

Prior to C++20, I would recommend tag dispatch to private delegating constructors as shown in @SamVarshavchik's answer.

Once you may use C++20, you can easily construct these kind of relations using a requires clause:

#include <type_traits>

template <bool IsIntegral>
struct Base  {
    Base(int param) requires IsIntegral {}
    Base() requires (!IsIntegral) {}
};

template<typename T> 
class CL : Base<std::is_integral<T>::value> {
    static constexpr bool kIsIntegral = std::is_integral_v<T>;
 public:
    CL() requires kIsIntegral : CL::Base(4) {}
    CL() requires (!kIsIntegral) : CL::Base() {}    
};

Upvotes: 1

Mario Demontis
Mario Demontis

Reputation: 484

Your derived class should be:

template<class T> class CL :
Base<is_integral<T>::value>{
  public:
     using base_t = Base<is_integral<T>::value>;
     using base_t::base_t;
};

The using base_t::base_t; makes you inherit the constructors from your base class (available since c++11).

In your example you should also change:

CL<int> c; // integral

into, e.g.:

CL<int> c(10); // integral

Upvotes: 1

Sam Varshavchik
Sam Varshavchik

Reputation: 118340

After playing around with this, a little bit, I came up with this, which I guess falls under the category of "workaround", which you are willing to consider:

template<class T> class CL :
    Base<is_integral<T>::value>
{
public:
    CL() : CL{ is_integral<T>{} } {}

    template<typename U=T>
    CL(std::true_type) : Base<true>{4} {}

    template<typename U=T>
    CL(std::false_type)
    {
    }
};

Upvotes: 1

Related Questions