Currently I have two functions :
template bool f(Type* x);
template bool f(std::tuple* x);
Sure, using is_specialization_of (link taken and fixed from here):
template<typename Type, bool IsTuple = is_specialization_of<Type, std::tuple>::value>
bool f(Type* x);
The question is, however, do you really want that? Normally, if you need to know if a type is a tuple, you need special handling for tuples, and that usually has to do with its template arguments. As such, you might want to stick to your overloaded version.
Edit: Since you mentioned you only need a small portion specialized, I recommend overloading but only for the small special part:
template<class T>
bool f(T* x){
// common parts...
f_special_part(x);
// common parts...
}
with
template<class T>
void f_special_part(T* x){ /* general case */ }
template<class... Args>
void f_special_part(std::tuple<Args...>* x){ /* special tuple case */ }
With C++17, here is a fairly simple solution using if constexpr
template <typename> struct is_tuple: std::false_type {};
template <typename ...T> struct is_tuple<std::tuple<T...>>: std::true_type {};
Then you can do something like:
template<typename Type> bool f(Type* x) {
if constexpr (is_tuple<Type>::value) {
std::cout << "A tuple!!\n";
return true;
}
std::cout << "Not a tuple\n";
return false;
}
A test to ensure it worked:
f(&some_tuple);
f(&some_object);
Output:
A tuple!!
Not a tuple
Solution taken in part from an answer found here: How to know if a type is a specialization of std::vector?
You could just have your functions defer to another function:
template<typename Type,bool IsTuple> bool f(Type *x);
template<typename Type>
inline bool f(Type* x) { return f<Type,false>(x); }
template<typename... List>
inline bool f(std::tuple<List...>* x) { return f<std::tuple<List...>,true>(x); }
Might be a little late but you can also do something like this, in a more modern c++17 style with template variables:
template <typename T>
constexpr bool IsTuple = false;
template<typename ... types>
constexpr bool IsTuple<std::tuple<types...>> = true;
And some tests
struct TestStruct{};
static_assert(IsTuple<int> == false, "Doesn't work with literal.");
static_assert(IsTuple<TestStruct> == false, "Doesn't work with classes.");
static_assert(IsTuple<std::tuple<int, char>>, "Doesn't work with plain tuple.");
static_assert(IsTuple<std::tuple<int&, char&>>, "Doesn't work with lvalue references");
static_assert(IsTuple<std::tuple<int&&, char&&>>, "Doesn't work with rvalue references");
You can view it here https://godbolt.org/z/FYI1jS
EDIT: You will want to run std::decay, std::remove_volatile, std::remove_const to handle special cases.
With C++11, this is my preferred pattern:
// IsTuple<T>()
template <typename T>
struct IsTupleImpl : std::false_type {};
template <typename... U>
struct IsTupleImpl<std::tuple <U...>> : std::true_type {};
template <typename T>
constexpr bool IsTuple() {
return IsTupleImpl<decay_t<T>>::value;
}
Works great. No dependencies (I can't use Boost).