Reputation: 40058
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
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
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