gexicide
gexicide

Reputation: 40058

C++ single constructor template specialization

Sorry if this is a dupe. There are many similar questions but it seems that none really addresses this problem. They are all a bit different.

So what I want to achieve: Consider a template class X<T>. Now, I want that an extra constructor extists for a specific instanciattion of that template, lets say for pointers X<T*>. I do not want to create a whole template specification for X<T*>, because X<T*> should be exactly as the usual template (and that template is gigantic, so copy pasting would be quite a code duplication smell), only that it has an extra constructor. I also do not want to make X<T*> inherit from X<T> because I do not want to establish a subtype relation between these two. Is this possible? I tried it like this:

template<T> class X{};

template<T> X<T*>::X(int i){...}

but it doesnt compile. Is this somehow possible?

Upvotes: 1

Views: 971

Answers (2)

Tanner Sansbury
Tanner Sansbury

Reputation: 51881

Here is a solution that uses boost but does not require C++11. In either case, X has a constructor that takes a single argument. When T is a pointer, the argument it expects is an int. When T is not a pointer, the argument is a type that is inaccessible to the caller.

#include <boost/type_traits.hpp>

template < typename T >
class X
{
private:
  // Declare a private type that will be used to prevent a single-argument 
  // constructor from being callable when T is not a pointer.
  struct T_must_be_a_pointer;
public:
  // Branch at compile-time based on T:
  // - If T is a pointer, then declare the X( int ) constructor.
  // - If T is not a pointer, then declare X( T_must_be_a_pointer ) constructor.
  explicit X( typename boost::mpl::if_< boost::is_pointer< T >,
                                        int,
                                        T_must_be_a_pointer >::type i )
  {}
};

int main() {
    X<int> a();
    // Next line will fail to compile:
    // X<int> b(1);
    X<int*> c(2);
    return 0;
}

Upvotes: 0

mfontanini
mfontanini

Reputation: 21900

You can do something like this, using SFINAE:

#include <iostream>
#include <type_traits>

template<class T> class X{
public:
    X(int i) {
        init();
    }
private:
    template<class U = T>
    typename std::enable_if<std::is_pointer<U>::value>::type init() {
        std::cout << "It's a pointer!\n";
    }

    template<class U = T>
    typename std::enable_if<!std::is_pointer<U>::value>::type init() {
        std::cout << "It's not a pointer!\n";
    }
};

int main() {
    X<int> a(1);
    X<int*> b(2);
}

Which outputs:

It's not a pointer!
It's a pointer!

You're not overloading the constructor, but you're achieving what you want.

Note that you require C++11 to use this code.

Edit: Okay, this code does exactly what you want:

#include <iostream>
#include <type_traits>

template<class T> class X{
public:
    template<class U = T, class enabler = typename std::enable_if<std::is_pointer<U>::value, T>::type>
    X(int i) {
        std::cout << "It's a pointer!\n";
    }

    template<class U = T, class enabler = typename std::enable_if<!std::is_pointer<U>::value, T*>::type>
    X() {
        std::cout << "It's not a pointer!\n";
    }
};

int main() {
    X<int> a;
    X<int*> b(2);
}

Which still outputs the same as before. Note that this is not quite a good design. Having some constructors depending on your template argument is odd. This code solves your problem though.

Upvotes: 5

Related Questions