NarekMeta
NarekMeta

Reputation: 103

Need a way to check template type and if not in the range of allowed types then have compile time error in C++

I need to have a template class where each object adds itself to a vector and based on the template type parameter(allowed only: string, int, float) add to the corresponding container. I need a way to have compile time checks for the type and based on the check add to the corresponding container and if the type is not one of the allowed types compile time error should be emitted. Example: code

vector<myClass<int>*> intVec;
vector<myClass<float>*> floatVec;
vector<myClass<string>*> stringVec;

template<typename T>
struct myClass
{
  myClass()
  {
     /*pseudo code
     if(T == int) {
       intVec.push_back(this);
     }
     else if(T == float) {
       floatVec.push_back(this);
     }
     else if(T == string){
       stringVec.push_back(this);
     }
     else {
       // error
     }
     */
  }
  
  T value;
 }

How can I achieve this ?

Upvotes: 0

Views: 124

Answers (3)

rustyx
rustyx

Reputation: 85341

Use specialization and a helper function, e.g.

template<typename T>
struct myClass;

inline std::vector<myClass<int>*> intVec;
inline std::vector<myClass<float>*> floatVec;
inline std::vector<myClass<std::string>*> stringVec;

template<typename T>
void add(myClass<T>*);

template<>
void add(myClass<int>* p) {
    intVec.push_back(p);
}

template<>
void add(myClass<float>* p) {
    floatVec.push_back(p);
}

template<>
void add(myClass<std::string>* p) {
    stringVec.push_back(p);
}

template<typename T>
struct myClass
{
    myClass()
    {
        add(this);
    }

    T value;
};

Upvotes: 1

apple apple
apple apple

Reputation: 10591

in addition to existing answers, you can also do it with normal function overloading

template<typename T>
struct myClass;

inline std::vector<myClass<int>*> intVec;
inline std::vector<myClass<float>*> floatVec;
inline std::vector<myClass<std::string>*> stringVec;

/* optional
template<typename T>
constexpr bool always_false = false;

template<typename T>
void add(myClass<T>*) {
    static_assert(always_false<T>,"unsupported T");
}
*/

void add(myClass<int>* p) {
    intVec.push_back(p);
}

void add(myClass<float>* p) {
    floatVec.push_back(p);
}

void add(myClass<std::string>* p) {
    stringVec.push_back(p);
}

template<typename T>
struct myClass
{
    myClass()
    {
        add(this);
    }

    T value;
};

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 596196

In C++17 and later, you can use if constexpr and std::is_same_v, eg:

#include <type_traits>

template<typename T>
struct myClass
{
  myClass()
  {
     if constexpr (std::is_same_v<T, int>) {
       m_intVec.push_back(this);
     }
     else if constexpr (std::is_same_v<T, float>) {
       m_floatVec.push_back(this);
     }
     else if constexpr (std::is_same_v<T, std::string>){
       m_stringVec.push_back(this);
     }
     else {
       // error
     }
  }
  
  T value;
};

In earlier versions, you can use either template specialization or SFINAE instead, eg:

// via specialization

template<typename T>
struct myClass
{
};

template<>
struct myClass<int>
{
  myClass()
  {
    m_intVec.push_back(this);
  }

  int value;
};

template<>
struct myClass<float>
{
  myClass()
  {
    m_floatVec.push_back(this);
  }

  float value;
};

template<>
struct myClass<std::string>
{
  myClass()
  {
    m_stringVec.push_back(this);
  }

  std::string value;
};
// via SFINAE

#include <type_traits>

template<typename T>
struct myClass
{
  template<typename U = T, std::enable_if<std::is_same<U, int>::value, int>::type = 0>
  myClass()
  {
    m_intVec.push_back(this);
  }

  template<typename U = T, std::enable_if<std::is_same<U, float>::value, int>::type = 0>
  myClass()
  {
    m_floatVec.push_back(this);
  }

  template<typename U = T, std::enable_if<std::is_same<U, std::string>::value, int>::type = 0>
  myClass()
  {
    m_stringVec.push_back(this);
  }

  T value;
};

Upvotes: 3

Related Questions