How can I check if a template's parameter is empty? Or of a "certain type"?

I'd prefer to check if it's empty or not rather than the type because if the wrong type is entered, it'd throw an error anyway..

But I definitely need to know if the parameter passed to the template is blank.

Why?

My Code:

void Insert() {/*Just for recurssion*/  /* Provided by Deceptikon */}
        template<typename T, typename... Args>
        DOSTUFF& Insert(T First, const Args... Remaining)
        {
            this->Meh.push_back(First);    //Error will be here if it's empty..
            Insert(Remaining...);
            return *this;
        }

So again.. Three questions:

1. How do I check the Template's parameter type?
2. How do I restrict the Template's parameter to a specific type only? //Though this breaks the purpose of a template ={
3. How do I check if the Template's Parameter is Blank/Null/Empty?

Recommended Answers

All 6 Replies

I think that the best way to do this is probably through another function definition. Basically you can use your function, but also declare one like this:

DOSTUFF& Insert()
{
    //whatever you do with no parameters
}

I am not sure if it would work, but I would think that the compiler would automatically match Insert() to the void version rather than try to fit the templated version. To check the type I think you can use type_id or something like that, do some research and you should be able to find it... if not I am sure that someone else on this forum knows the answer. To restrict templates to a specific type I refer you to http://www.learncpp.com/cpp-tutorial/146-partial-template-specialization/, it is quite informative. Hope this helps!

Uhh well that's the thing.. I want to allow "empty parameters" and that's why I need to check.. so that I can do:

class DoStuff
{
	private:
		void Insert(){}
		vector<Types> Meh;
		
	public:
	    DoStuff() {}           //This constructor doesn't get kicked in due to the one below it..
		
        template<typename T, typename... Args>
        DoStuff& Insert(T First, const Args... Remaining)
        {
           if !(Template parameter empty)
           {
            this->Meh.push_back(First);    //Error will be here if it's empty..
            Insert(Remaining...);
           }
           else
             Meh.push_back(i(0));
        }
}

int main()
{
    DoStuff P();   //This should be DoStuff P;    I just solved it but learned lots from your posts above.. THANKS! +1
}

I'll check out what you guy's suggested. And it doesn't matter if you're wrong lately. I seriously appreciate the reply :)

DoStuff P(); //This should be DoStuff P; I just solved it but learned lots from your posts above.. THANKS! +1

You can check for allowed types at compile time using something like this:

template< typename T > struct type_validator;

template<> struct type_validator< int > {};

template< typename T >
T f( const T& x )
{
    type_validator< T >();

    return x;
}

int main()
{
    int a = 2;
    f(a);   // OK

    char z = 'a';
    f(z);   // Compilation error!

    return 0;
}

You can also use BOOST_STATIC_ASSERT() if you want to get nice messages when you do something you're not allowed to.

commented: You sir just made me bookmark this for future reference =] +6

>> 1. How do I check the Template's parameter type?

Using typeid is one way to do it, but a rather unusual one. It is more common to use one of the multiple type-traits that are provided by the <type_traits> library (C++11 only), and/or the ones provided by Boost.TypeTraits. As a simple example:

template <typename T>
T square_value(T value) {
  if( boost::is_complex< T >::value )
    return conj(value) * value;
  else
    return value * value;
};

The more useful type-traits include checking if the type has cv qualifiers, checking if it is convertible to some other type, to check if it is a pointer type, or reference type, etc... It is rather rare that you would want to check for one specific type, unless what you really want is to have a specialization (or overload in the case of function templates) for a given type.

Two other useful techniques are the BOOST_STATIC_ASSERT and BOOST_CONCEPT_ASSERT . In the first case, you can just check a condition at compile-time, like BOOST_STATIC_ASSERT(( typeid(T) == typeid(int) )); which will fail with a reasonably error message if the condition is not met. In the second case, you can assert that a given type satisfies a certain "concept", meaning that it can be used in certain ways, like BOOST_CONCEPT_ASSERT(( boost::CopyConstructibleConcept<T> )); to check if the type is copy-constructible for example.


>> 2. How do I restrict the Template's parameter to a specific type only?

There are a number of techniques for this.

First of all, if you want to restrict a template parameter to a specific type, then it should not be a template parameter, there has to be some freedom in what the type could be, otherwise there is no point in making it a template to begin with. So, I'm assuming that you mean it in a wider sense of restricting the parameter to some _kind_ of types.

The most basic way is to provide overloaded versions for each specific type. Here's an example of an advance function ('advance' as in, the std::advance function) which would naturally require one algorithm for a random-access iterator and another for a forward-iterator. You could do:

// random-access version with a vector iterator:
template <typename T, typename Allocator>
void advance(std::vector<T,Allocator>::iterator& it, int n) {
  it += n;
};

// forward-access version with a list iterator:
template <typename T, typename Allocator>
void advance(std::list<T,Allocator>::iterator& it, int n) {
  for(int i = 0; i < n; ++i) 
    ++it;
};

Clearly, the above works in the sense that the first function is restricted to only iterators from a vector type, and the second function is restricted to list-iterators. However, the above is not very generic because there are plenty of random-access iterators which are not vector-iterators, idem for forward iterators. Note that you can also disable specific parameter types by providing an overload that will be selected instead of the general template, and just by inserting invalid code in it you can disable it:

template <typename T, typename Allocator>
void advance(std::list<T,Allocator>::iterator&, int) {
  char sorry_no_advance_algorithm_for_list_iterators[0];  //declaring a 0-sized static array is illegal.
};

The above will cause a compilation error if you try to use advance with a list iterator, and the "sorry_no_advance_algorithm_for_list_iterators" will appear in the error message somewhere.

The above are simple techniques that can work in simple situations, like disabling a function for one specific type or class template, or providing a specialization for one specific type (after all, this is what you do if you overload the << or >> operators on iostreams).

Another more powerful and much more common way to distinguish types is to use a technique called "tag-dispatching". This is how the actual std::advance function is implemented, the STL uses tag-dispatching for just about every situation like this one. The underlying mechanism is to dispatch (or select an overload) based on some type (a tag) that is associated to the template parameter type (and you usually access this associated type with a "traits" class template, like std::iterator_traits<> ).

Finally, a more fancy technique which is somewhat more direct than tag-dispatching and is just as powerful, is Sfinae (or Sfinae-switching) (stands for: Substitution Failure Is Not An Error). The underlying mechanism is to you force the substitution of the function's template arguments (to form the parameters and return types) to fail if the type(s) don't meet some requirement. The standard requires that if this substitution fails, the compiler must simply continue to look for a better overloaded version of the function, and not give a compilation error (thus the name sfinae). In practical terms, this might translate to:

template <typename Iter, typename Distance>
typename boost::enable_if<
  boost::has_plus_assign< Iter, Distance >,
void >::type advance(Iter& it, Distance n) {
  it += n;
};

template <typename Iter, typename Distance>
typename boost::disable_if<
  boost::has_plus_assign< Iter, Distance >,
void >::type advance(Iter& it, Distance n) {
  for(int i = 0; i < n; ++i) 
    ++it;
};

The enable_if / disable_if are simply templates that cause a compilation error if the first meta-value is false. In fact, here is it's implementation (this is the real thing from Boost, it's as simple as that!):

template <bool B, class T = void>
  struct enable_if_c {
    typedef T type;
  };

  template <class T>
  struct enable_if_c<false, T> {};

  template <class Cond, class T = void> 
  struct enable_if : public enable_if_c<Cond::value, T> {};

You see, if the condition is false, then the empty class is instantiated and accessing the nested type type will fail, thus disabling the function (the disable_if is implemented in the opposite way, of course).


>> 3. How do I check if the Template's Parameter is Blank/Null/Empty?

That situation will never occur unless you are using variadic templates. If you are using variadic templates for a function template, then you do as deceptikon showed: you terminate the recursion with an function overload which takes no parameters.

If you are using variadic template for a class template, then you create a specialization for an empty template parameter list. Say you implemented a tuple's class, you could do:

template <typename... Args>
class my_tuple {
  //... some implementation.
};

template <>
class my_tuple<> {
  //.. some implementation that is specific to the 'empty' case, or simply terminates a recursion of some kind.
};
commented: you sir, owned me +5

Uhh well that's the thing.. I want to allow "empty parameters" and that's why I need to check.. so that I can do:

class DoStuff
{
	private:
		void Insert(){}
		vector<Types> Meh;
		
	public:
	    DoStuff() {}           //This constructor doesn't get kicked in due to the one below it..
		
        template<typename T, typename... Args>
        DoStuff& Insert(T First, const Args... Remaining)
        {
           if !(Template parameter empty)
           {
            this->Meh.push_back(First);    //Error will be here if it's empty..
            Insert(Remaining...);
           }
           else
             Meh.push_back(i(0));
        }
}

int main()
{
    DoStuff P();   //This should be DoStuff P;    I just solved it but learned lots from your posts above.. THANKS! +1
}

I'll check out what you guy's suggested. And it doesn't matter if you're wrong lately. I seriously appreciate the reply :)

DoStuff P(); //This should be DoStuff P; I just solved it but learned lots from your posts above.. THANKS! +1

Also a easy way to solve this problem is to make make sure there is at least 1 argument for the second constructor to be called. Example,

template<typename T, typename... Args>
DoStuff& Insert(T First, const Args& arg0, const Args... Remaining){
  //...
}

I think that should work, no guarantees.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.