Vincent
Vincent

Reputation: 60381

Detect whether a type is an zero-element array?

Consider the following function :

template <typename Type>
void f(const Type& x)

I would like to do something special (without specialization) in it whether the passed type is an empty std::tuple or an empty std::array. For a tuple of nu elements, I can use std::is_same<Type, std::tuple<>>::value but what trick can I use to detect an zero-element array ?

(I am searching for a solution that do not require the creation of another function or class, ...)

Upvotes: 3

Views: 355

Answers (2)

Daniel Frey
Daniel Frey

Reputation: 56863

You can use std::tuple_size, as it will also work for std::array! See here. Simply use:

std::tuple_size<Type>::value == 0

to check if Type is an empty std::tuple<> or an empty std::array<T,0>.


With the above, the question remains what happens if Type is neither a std::tuple not a std::array. The general approach I see is this:

constexpr bool IsNotTupleOrArray =
  !std::is_class<Type>::value ||
  std::is_same<Type,ExcludeThisClass>::value ||
  sizeof(Type)>1 || // non-portable, works for GCC 4.8+
  ...;

std::conditional< IsNotTupleOrArray,
                  std::false_type,
                  std::tuple_size<Type> >::type::value;

which basically means that you have to explicitly exclude other types. For example is_class<Type> excludes all fundamental types like int, pointers, etc.

Upvotes: 6

Andy Prowl
Andy Prowl

Reputation: 126442

If you don't want to do it without specialization, why not doing it with overload? ;) Specializing a function template is sometimes (no, often) a bad idea. With some SFINAE trick it wouldn't be difficult to create an overload that is selected only when those two conditions apply.

However, I already hear you shouting that it's not what you wanted to do: what you wanted is some kind of if inside of f() that would be executed in case the condition is true, and a corresponding else branch that would be executed when the condition is false.

However, notice that this would not be a static if, but a regular run-time if: in other words, the compiler would know at compile-time with 100% certainty that one of those two branches is never going to be executed (and it would probably issue an annoying warning about it), but it will have to parse both branches and prove them legal.

In practice, this means that you won't be able to put statements that depend on the particular type T (or on properties of T) in order to be compilable. For instance, in the code below compile_time_expression determines whether type T has a member function foo() or a member function bar():

T obj;
if (compile_time_expression)
{
    obj.foo();
}
else
{
    obj.bar();
}

However, the above won't compile if that particular T doesn't have both a member function foo() and a member function bar(): since what you have here is a run-time if, the compiler will have to parse both branches and make sure they're both compilable - and then possibly optimize away the one that is never going to be executed.

Unfortunately, C++ does not have any construct such as static if (yet?), so overloading + SFINAE is the right way to tackle this problem.

Upvotes: 3

Related Questions