20

Currently I have two functions :

template<typename Type> bool f(Type* x);
template<typename... List> bool f(std::tuple<List...>* x);

Is there any way to merge these two functions with an extra template parameter that indicates whether the passed type is a tuple ?

template<typename Type, bool IsTuple = /* SOMETHING */> bool f(Type* x);
Vincent
  • 57,703
  • 61
  • 205
  • 388

5 Answers5

18

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 */ }
Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • In normal time this could be dangerous and a specialization would be preferable. But this function is a large function, and only a small `if` inside change whether tuple type or not. – Vincent Oct 27 '12 at 14:38
  • Using an extra template parameter also means it's possible to explicitly call `f, false>`, or `f`, which could be avoided by checking `is_specialization_of` in the function body. –  Oct 27 '12 at 14:38
  • @Vincent: Then I'd actually recommend tag-dispatching instead of a runtime `if`. :) – Xeo Oct 27 '12 at 14:40
  • 5
    `static if`, we need yooooooou – Seth Carnegie Oct 27 '12 at 14:54
  • 2
    in C++ 17, you can use `constexpr if` – Nishant Singh Oct 25 '18 at 08:29
16

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?

smac89
  • 39,374
  • 15
  • 132
  • 179
6

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).

Dave Dopson
  • 41,600
  • 19
  • 95
  • 85
5

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); }
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
4

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.

David Ledger
  • 2,033
  • 1
  • 12
  • 27