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
Why Don't Std::Vector's Elements Need a Default Constructor
Comparing 3 Modern C++ Ways to Convert Integral Values to Strings
Implicit Conversion When Overloading Operators for Template Classes
Std::Forward_List and Std::Forward_List::Push_Back
Looking for 16-Bit X86 Compiler
C++Cli. Are Native Parts Written in Pure C++ But Compiled in Cli as Fast as Pure Native C++
Initialize Global Array of Function Pointers at Either Compile-Time, or Run-Time Before Main()
C++ Inserting Unique_Ptr in Map
Return Statement in Ternary Operator C++
Install Gcc on Linux with No Root Privilege
Should the Copy-And-Swap Idiom Become the Copy-And-Move Idiom in C++11
C++11 Rvalue Reference Calling Copy Constructor Too
Is a Linked-List Implementation Without Using Pointers Possible or Not