Type Condition in Template

Type condition in template

Since C++17 there is a way to do exactly this with if-constexpr. The following compiles since clang-3.9.1, gcc-7.1.0, and recent MSVC compiler 19.11.25506 handles well too with an option /std:c++17.

#include <iostream>
#include <type_traits>

template<typename T>
void printType(T)
{
if constexpr (std::is_same_v<T, const char*>)
std::cout << "const char*" << std::endl;
else if constexpr (std::is_same_v<T, int>)
std::cout << "int" << std::endl;
else
std::cout << "???" << std::endl;
}

int main()
{
printType("Hello world!");
printType(1);
printType(1.1);
return 0;
}

Output:

const char*
int
???

Template class with conditional typenames

Typically you'd do this by creating a trait type whose specializations define the additional types. For example:

// Base template is undefined.
template <typename T>
struct optix_traits;

template <>
struct optix_traits<float> {
using dim2 = optix::float2;
// etc
};

template <>
struct optix_traits<double> {
using dim2 = optix::double2;
// etc
};

Then you can alias from these types to a name in your type, if desired:

template <typename T>
class MyClass {
public:
using T2 = typename optix_traits<T>::dim2;
};

C++ template with condition for type

You are most of the way there already. In order to overload SFINAE, you need to use a non-type template parameter with an assigned default value instead of using defaulted type template parameter.

So, we change both functions to use a non-type paramere with a default value, and just negate the condition like:

template<typename ValueType>
class Comparator {
public:

...
template<
typename T = ValueType,
std::enable_if_t<
!std::is_floating_point<std::remove_reference_t<ValueType>>::value,
// ^- not here, so only non-floating point types allowed
bool> = true // set the bool to true, this lets us call the function without providing a value for it
>
bool passed(T actualValue) { non floating point code here }

template<
typename T = ValueType,
std::enable_if_t<
std::is_floating_point<std::remove_reference_t<T>>::value,
bool> = true // set the bool to true, this lets us call the function without providing a value for it
>
bool passed(T actualValue, T eps) { floating point code here }

Type condition in C++ template class problem

In the second case, what RET is, depends on the template type T. The compiler needs to be assured that it is going to be a type in all possible instantiations (and not perhaps a static member of some instantiation of IF). You do so with the typename keyword.

template <typename T>
class Param
{
typename IF< sizeof(int)<sizeof(long), T&, T* >::RET mParam;

};

template class default type and condition

You can add static_assert to perform the checking.

template <typename T=std::conditional_t<sizeof(void*) == 8, std::uint64_t, std::uint32_t>>
class MyClass
{
static_assert(std::is_same_v<T, std::uint64_t> || std::is_same_v<T, std::uint32_t>, "T must be std::uint64_t or std::uint32_t");
private:
std::vector<T> vs;
public:
// ...
};

LIVE

condition in template function implementation depending on whether the type is a pointer

Tag dispatch:

template< typename T >
void AbstractEvent::setVar_impl( QString varName, T value, std::true_type /*is_ptr*/)
{
void * castValue = static_cast<void*>(value);
if (castValue)
{
//do sth with castValue
}
}

template< typename T >
void AbstractEvent::setVar_impl( QString varName, T value, std::false_type /*is_ptr*/)
{
//do something with value
}

template< typename T >
void AbstractEvent::setVar( QString varName, T value){
setVar_impl(varName, value, std::is_pointer<T>());
}

Alternatively, overload and then SFINAE out the inapplicable one:

template<typename T>
typename std::enable_if<std::is_pointer<T>::value>::type
AbstractEvent::setVar(QString varName, T value)
{
void * castValue = static_cast<void*>(value);
if (castValue)
{
//do sth with castValue
}
}

template< typename T>
typename std::enable_if<!std::is_pointer<T>::value>::type
AbstractEvent::setVar( QString varName, T value)
{
//do something with value
}

Third alternative, overload directly:

template< typename T >
void AbstractEvent::setVar( QString varName, T* value)
{
void * castValue = static_cast<void*>(value);
if (castValue)
{
//do sth with castValue
}
}

template< typename T >
void AbstractEvent::setVar( QString varName, T value)
{
//do something with value
}

The first template is more specialized than the second by partial ordering, so will be chosen if both are equally viable.

You may also want to special case the T == nullptr_t case. nullptr_t isn't a pointer type, but you probably want the pointer overload to be called in that situation.

Type depends on condition in template

You only have to add a ::type and a typename

template <bool two>
// ......*typename*.....................................................*::type*
void foo (typename std::conditional<two, double*, std::complex<double>* >::type input, size_t n)
{
for (size_t i = 0; i < n; ++n)
input[i] *= two ? 2.0 : 1.0;
}

Template overload based on condition

What I want to do is

template<typename Rect, typename Size /*if Size::width, use this*/>
Rect& move(Rect& rc, Size size);
template<typename Rect, typename Point /*if Point::x, use this*/> 
Rect& move(Rect& rc, Point to);

I.e. choosing an overload depends on whether a template argument has a particular member. Is it possible in c++?

If you can use at least C++11... have you tried with SFINAE through trailing return type and decltype()?

I mean... something as

template <typename Rect, typename Size>
auto move (Rect & rc, Size size)
-> decltype( size.width, rc );
// .............^^^^^^^^^^^ <-- note this

template <typename Rect, typename Point>
auto move(Rect& rc, Point to)
-> decltype( to.x, rc );
// .............^^^^^ <-- and note this

Obviously this doesn't works if you call move() with a second argument with both a with and a x member: the compiler doesn't know which one move() select.

How it works?

It's simple: the main word is SFINAE, that mean Substitution Failure Is Not An Error.

Take in count that decltype() return the type of the contained expression, so (for example) from

   decltype( size.width, rc );

the comma operator discard size.width, if available (this is the important point!), and remain rc, so decltype() return the type of rc (if size.width exist!).

But what happens if size.width doesn't exist?

You have a "substitution failure". That "is not an error" but remove this overloading version of move() from the set of available move() functions.

how to do an if else depending type of type in c++ template?

You could use typeid:

if (typeid(T) == typeid(int))

Or you could use the std::is_same type trait:

if (std::is_same<T, int>::value)


Related Topics



Leave a reply



Submit