Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I know the C++ template is a container or a type? [duplicate]

Tags:

c++

templates

stl

I give the following codes to show my question:

template<T>
void my_fun(T &obj)
{
  if(obj is a type like float, std::string, double)
   {
       perform1()
  }
  if(obj is a container like std::vector, std::list)
  {
      perform2()
 } 
}
std::vector<int> abc;
my_fun(abc);
int d;
my_fun(d);

Then my questions, how can I know the template refers to a simple type or a container? Thanks.

like image 531
feelfree Avatar asked Jun 28 '16 13:06

feelfree


People also ask

What kind of container is the STL vector?

1) std::vector is a sequence container that encapsulates dynamic size arrays.

Is std list doubly linked?

std::list is a container that supports constant time insertion and removal of elements from anywhere in the container. Fast random access is not supported. It is usually implemented as a doubly-linked list.

Is string part of STL?

The string class is not part of the STL, but has implemented many STL features. string can be treated as a STL container of char .


2 Answers

You can write your own trait and enable_if on it via expression SFINAE with multiple overloads. Here is a solution that uses the void_t trick (which will presumably appear in C++17):

#include <iostream>
#include <type_traits>
#include <vector>

template<typename ...>
using to_void = void; // maps everything to void, used in non-evaluated contexts

template<typename T, typename = void>
struct is_container : std::false_type
{};

template<typename T>
struct is_container<T,
        to_void<decltype(std::declval<T>().begin()),
                decltype(std::declval<T>().end()),
                typename T::value_type
        >> : std::true_type // will  be enabled for iterable objects
{};

template<typename T>
void f(T param, typename std::enable_if<is_container<T>::value>::type* = nullptr)
{
    std::cout << "Container\n";
}

template<typename T>
void f(T param, typename std::enable_if<std::is_fundamental<T>::value>::type* = nullptr)
{
    std::cout << "Fundamental\n";
}

template<typename T>
void f(T param, 
    typename std::enable_if<!std::is_fundamental<T>::value>::type* = nullptr, 
    typename std::enable_if<!is_container<T>::value>::type* = nullptr)
{
    std::cout << "Other\n";
}

struct Foo{};

int main()
{
    int x{};            // fundamental
    std::vector<int> v; // container
    Foo s{};    // other

    f(x);
    f(v);
    f(s);
}

Live on Coliru

like image 169
vsoftco Avatar answered Nov 11 '22 10:11

vsoftco


You have several options at your disposal.

  • If you want a default behaviour, and change it only for one type (or a few), use template specialization for your function.

For example :

template<typename T>
void myfunc() { /*default*/ }


template<>
void myfunc<int>() { /*specialized version for int */}
  • If you want to change the behaviour of your functions for generic groups of types, you can use Type Traits (something like std::is_fundamental ). You might have to implement your own type traits in thiscase.
like image 23
Louen Avatar answered Nov 11 '22 10:11

Louen