How to Do an If Else Depending Type of Type in C++ Template

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)

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;
};

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;

};

conditional type define in c++?

In C++20, you can just use concepts

template<typename Container>
struct B { };

template<typename Container>
requires requires { typename Container::NestedTypeA; }
struct B<Container> {
using NestedType = typename Container::NestedTypeA;
};

template<typename Container>
requires requires { typename Container::NestedTypeB; } &&
(!requires { typename Container::NestedTypeA; })
struct B<Container> {
using NestedType = typename Container::NestedTypeB;
};

template<typename Container>
requires requires { typename Container::NestedTypeC; } &&
(!requires { typename Container::NestedTypeA; }) &&
(!requires { typename Container::NestedTypeB; })
struct B<Container> {
using NestedType = typename Container::NestedTypeC;
};

template<typename Container>
class A : public B<Container> {};

Demo

In C++17, you can use std::void_t to detect the validity of member types.

#include <type_traits>

template<typename Container, typename = void>
constexpr bool HasNestedTypeA = false;
template<typename Container>
constexpr bool HasNestedTypeA<
Container, std::void_t<typename Container::NestedTypeA>> = true;

template<typename Container, typename = void>
constexpr bool HasNestedTypeB = false;
template<typename Container>
constexpr bool HasNestedTypeB<
Container, std::void_t<typename Container::NestedTypeB>> = true;

template<typename Container, typename = void>
constexpr bool HasNestedTypeC = false;
template<typename Container>
constexpr bool HasNestedTypeC<
Container, std::void_t<typename Container::NestedTypeC>> = true;

template<
typename Container,
bool = HasNestedTypeA<Container>,
bool = HasNestedTypeB<Container>,
bool = HasNestedTypeC<Container>>
struct B { };

template<typename Container>
struct B<Container, false, false, false> {};

template<typename Container, bool B1, bool B2>
struct B<Container, true, B1, B2> {
using NestedType = typename Container::NestedTypeA;
};

template<typename Container, bool B1>
struct B<Container, false, true, B1> {
using NestedType = typename Container::NestedTypeB;
};

template<typename Container>
struct B<Container, false, false, true> {
using NestedType = typename Container::NestedTypeC;
};

template<typename Container>
class A : public B<Container> {};

Demo

Returning different type from a function template depending on a condition

Types in C++ are determined at compile time. That means that a runtime condition cannot be used to infer the type of an object.

Seeing how you tried to use templates suggest to me that there's a little misunderstanding here. Template code is instanciated by the compiler. Code in templates is not really code until instanciated. If we were to do, by hand, the instanciation of you code using the type A, it would look close to something like this (not actual code):

template <>
auto getObject<A>() -> C<A> {
if(/* something at runtime */) {
return C<A>{};
} else {
return C<B>{};
}
}

// auto
C<A> obj = getObject<A>();

As you can see, the code in the else doesn't make sense. You cannot return a value of type C<B> inside a function that must return C<A>. As a side effect of compile time instanciation of code, C<A> and C<B> are unrelated and are completely different types.

Also, you can see that auto has been replaced by C<A>. This is because auto is also inferred at compile time.


Now... What can you do to make your code work?

There are multiple solution and abstraction to have a variable that has a runtime defined type. I'll cover some of the options you can use.

Using a variant

A variant is a class that can hold a single instance of a variable that can be of different types, specified in a finite list of types. For instance, a std::variant<int, std::string> is a variable that can either be a integer or a string.

In your code, it would be a variant of C<A> and C<B>:

auto getObject() -> std::variant<C<A>, C<B>> {
if (/* something at runtime */) {
return C<A>{};
} else {
return C<B>{};
}
}

auto obj = getObject();

// The type of obj is std::variant<C<A>, C<B>>

If you don't have access to C++17, you can always use boost::variant.

The downside of this solutions is that you have to know every types the variant can take. If you have a indefinite amount of types, you cannot use variant. It is however, extremely fast and promote regularity ( value semantics ).

virtual polymorphism

Virtual polymorphism is the most common way to get variables of different types decided at runtime. It looks nice but comes with the price of pointer and dynamic allocation, and is rather intrusive. It would look like this:

struct CC {
virtual ~CC() = default;
};

template<typename T>
struct C : CC {
T barcode;
};

auto getObject() -> std::unique_ptr<CC> {
if (/* something at runtime */) {
return std::make_unique<C<A>>();
} else {
return std::make_unique<C<B>>();
}
}

auto obj = getObject();

// The type of obj is std::unique_ptr<CC>

Note that if this is what you want to do, you have to define some common interface in CC. The rest of the code will use that common interface in order to do operations on C and it's barcode.

Please note that std::make_unique is part of C++14. It can be replaced by std::unique_ptr<C<A>>{new C<A>} and std::unique_ptr<C<B>>{new C<B>} respectively.


There is also std::any and other form of type erasure technique available. This answer is already quite long. There is plenty of documentation you can find online for free that describes all of this in depth.

How to implement If-Else Conditional template?

DoMoveRL<G, M>::result forces instantiation of DoMoveRL<G, M>.

You might delay the retrieval of result to avoid to force instantiation:

template<typename G, typename M>
struct DoMove {
private:
constexpr static bool _isRLMove = (M::direction == Direction::LEFT
|| M::direction == Direction::RIGHT);
public:
using result = typename Conditional<
_isRLMove, DoMoveRL<G, M>, DoMoveUD<G, M>>::value::result;
};

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.



Related Topics



Leave a reply



Submit