Reputation: 76
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
Reputation: 73186
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
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
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