Reputation: 1145
Is it possible to make compiler choose between template specifications depending on type traits? For example, consider two template implementations of a Compare functional, one for sequential types (string
s, vector
s, list
s, etc.) and another one for integer types. Can we have only one template specialization for the each class of types?
template <class SeqT>
class Compare
{
public:
bool operator()(const SeqT& s1, const SeqT& s2) const
{
typename SeqT::const_iterator it1=s1.begin();
typename SeqT::const_iterator it2=s2.begin();
while(it1!=s1.end() && it2!=s2.end())
{
if(*it1<*it2) return true;
if(*it2<*it1) return false;
++it1; ++it2;
}
return it2!=s2.end();
}
};
template <class IntegerT>
class Compare
{
public:
bool operator()(IntegerT i1, IntegerT i2) const
{
return i1<i2;
}
};
template <class T, class Cmp = Compare<T> >
class SomeContainer
{
...
};
Basically, what I am looking for is a way to partially specialize a template by imposing a condition on the template argument. Like the first Compare<>
specialization should be applied to the following types: std::basic_string<>
, std::vector<>
, std::list<>
, and the second for the following types: int
, unsigned
, short
, char
. Is that possible?
Upvotes: 3
Views: 3388
Reputation: 75697
This is my answer to another question, but it is what you need. It uses SFINAE to create template specialization ONLY for the template arguments that test true for a condition (like beeing a specific type).
https://stackoverflow.com/a/20898554/2805305
Edit
But how can I exactly specify that the first Compare specialization can be applied to e.g. std::basic_string and std::vector?
You create a trait that tells you if T is a vector or basic_string or list:
#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <complex>
#include <type_traits>
using namespace std;
template <class T>
struct is_seq : std::false_type {
};
template <class T>
struct is_seq<std::vector<T>> : std::true_type {
};
template <class T>
struct is_seq<std::basic_string<T>> : std::true_type {
};
template <class T>
struct is_seq<std::list<T>> : std::true_type {
};
template <class T>
using enable_if_seq_type = typename std::enable_if<is_seq<T>::value>::type;
template <class T>
using enable_if_integral_type = typename std::enable_if<std::is_integral<T>::value>::type;
template <class T, class Enable = void>
class Compare; // <-------- define if you want a Compare for any type that doesn't match any specialization
template <class T>
class Compare<T, enable_if_seq_type<T>> { // specialization for T a vector, string or list
public:
void foo() {
cout << "vector, string and list specialization" << endl;
}
};
template <class T>
class Compare<T, enable_if_integral_type<T>> { // specialization for T an integral type
public:
void foo() {
cout << "integral specialization" << endl;
}
};
int main() {
cout << std::boolalpha;
cout << is_seq<int>::value << endl; // false
cout << is_seq<std::vector<int>>::value << endl; // true
Compare<int> c1; // uses second specialization
c1.foo(); // output "integral specialization"
Compare<std::vector<int>> c2; // uses first specialization
c2.foo(); // output "vector, string and list specialization"
//Compare<std::complex<int>> c3;
// compile error if you just declare and not define the generic Compare.
// If you define the generic Compare, this will compile and it will use
// that definition
return 0;
}
If you want to be able to instantiate the class Compare
for any other type, then you define the first (general) Compare
declaration.
Upvotes: 3