huy
huy

Reputation: 4904

How do I check if my template class is a specific classtype?

In my function template, I'm trying to check whether the type T is a specific type. How would I do that?

template<class T> int foo(T a) {
  // check if T of type, say, String?
}

Upvotes: 25

Views: 51916

Answers (10)

JakobS.
JakobS.

Reputation: 128

You can use C++20 concepts like this:

template<typename T>
concept SpecialType = std::is_same_v<T, String>;

then you can have another function:

template<SpecialType T>
void foo(T t) {}

which only accepts templates which statisfy the concept above.

Upvotes: 1

Jan Schultke
Jan Schultke

Reputation: 39415

You can check if two types are the same with the std::is_same trait:

template<class T>
void foo(T a) {
    // C++17
    if constexpr (std::is_same_v<T, String>) { /* ... */ }
    // C++11
    if (std::is_same<T, String>::value) { /* ... */ }
}

Constraints on a function

In C++20, there is also the std::same_as concept:

// use the concept as a type constraint here
template <std::same_as<String> T>
void foo(T a);

// or with abbreviated function templates
void foo(std::same_as<String> auto a);

This would prevent you from calling a function with any type other than A. Note that std::enable_if can also emulate such C++20 type constraints. Checking whether the type is the same inside the function doesn't affect overload resolution.

Checking the dynamic type

Note that neither of the solutions above check the dynamic type, i.e. they don't account for polymorphic classes. This would require typeid:

if (typeid(a) == typeid(String))

Upvotes: 1

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506837

I suspect someone should tell you why it might not be a good idea to avoid using overloading or specialization. Consider:

template<class T> int foo(T a) {
  if(isAString<T>()) {
    return a.length();
  } else {
    return a;
  }
}

You might think on a first sight that it will work for int too, because it will only try to call length for strings. But that intuition is wrong: The compiler still checks the string branch, even if that branch is not taken at runtime. And it will find you are trying to call a member function on non-classes if T is an int.

That's why you should separate the code if you need different behavior. But better use overloading instead of specialization, since it's easier to get a clue how things work with it.

template<class T> int foo(T a) {
  return a;
}

int foo(std::string const& a) {
  return a.length();
}

You have also better separated the code for different paths of behavior. It's not all anymore clued together. Notice that with overloading, the parameters may have different type forms and the compiler will still use the correct version if both match equally well, as is the case here: One can be a reference, while the other can not.

Upvotes: 7

Beno&#238;t
Beno&#238;t

Reputation: 16994

If you don't care about compile-time, you may use boost::is_same.

bool isString = boost::is_same<T, std::string>::value;

As of C++11, this is now part of the standard library

bool isString = std::is_same<T, std::string>::value

Upvotes: 15

Dave Dopson
Dave Dopson

Reputation: 42684

If you are using C++11 or later, std::is_same does exactly what you want:

template <typename T>
constexpr bool IsFloat() { return std::is_same<T, float>::value; }

template <typename T>
void SomeMethodName() {
  if (IsFloat<T>()) {
    ...
  }
}

http://en.cppreference.com/w/cpp/types/is_same

Upvotes: 2

sergiom
sergiom

Reputation: 4877

I suppose you could use the std::type_info returned by the typeid operator

Upvotes: 6

visitor
visitor

Reputation: 8834

hmm because I had a large portion of same code until the 'specification' part.

You can use overloading, but if a large part of the code would work for any type, you might consider extracting the differing part into a separate function and overload that.

template <class T>
void specific(const T&);

void specific(const std::string&);

template <class T>
void something(const T& t)
{
    //code that works on all types
    specific(t);
    //more code that works on all types
}

Upvotes: 11

You can perform static checks on the type that you have received (look at the boost type traits library), but unless you use specialization (or overloads, as @litb correctly points out) at one point or another, you will not be able to provide different specific implementations depending on the argument type.

Unless you have a particular reason (which you could add to the question) not to use the specialization in the interface just do specialize.

template <> int subtract( std::string const & str );

Upvotes: 2

kennytm
kennytm

Reputation: 523164

You can check using type_traits (available in Boost and TR1) (e.g. is_same or is_convertible) if you really want to avoid specialization.

Upvotes: 4

dirkgently
dirkgently

Reputation: 111120

Instead of checking for the type use specializations. Otherwise, don't use templates.

template<class T> int foo(T a) {
      // generic implementation
}
template<> int foo(SpecialType a) {
  // will be selected by compiler 
}

SpecialType x;
OtherType y;
foo(x); // calls second, specialized version
foo(y); // calls generic version

Upvotes: 50

Related Questions