Reputation: 1777
I was working on a project recently and wanted to give precedence to certain types when performing operations other than what is standard. To do this I was attempting to use a template somehow to determine what kind of datatypes were being used. The code I have written clearly doesn't work, but it gets the idea of what I'm trying to do across
#include <iostream>
template <type1,type2>
typename determine(type1 a, type2 b)
{
if (typeid(type1) == typeid(int) || typeid(type2) == typeid(int))
return int;
else return double;
}
int main()
{
int a = 3;
double b = 2;
std::cout << (static_cast<determine(a, b)>(a) / static_cast<determine(a, b)>(b)) << std::endl;
}
Is there a way to make determine return something that I can actually use to decide what datatype to use?
Upvotes: 12
Views: 11492
Reputation: 50550
Here is another working solution you can use to do it.
The basic idea is to rely on function overloading and sfinae to dispatch the request and avoid multiple definitions.
It follows a minimal, working example:
#include<type_traits>
#include<utility>
#include<cassert>
template <typename T1, typename T2>
typename std::enable_if<std::is_same<T1, int>::value or std::is_same<T2, int>::value, int>::type
determineImpl(int, T1 a, T2 b) {
return 42;
}
template <typename T1, typename T2>
typename std::enable_if<not std::is_same<T1, int>::value and not std::is_same<T2, int>::value, double>::type
determineImpl(char, T1 a, T2 b) {
return .42;
}
template<typename... Args>
auto determine(Args&&... args) -> decltype(determineImpl(0, std::forward<Args>(args)...)) {
return determineImpl(0, std::forward<Args>(args)...);
}
int main() {
auto v1 = determine(42, 'c');
assert(v1 == 42);
auto v2 = determine('c', .42);
assert(v2 == .42);
}
Upvotes: 1
Reputation: 66224
I'm pretty sure you can do this with std::conditional
and a few std::is_same
qualifiers logically combined via ||
:
#include <iostream>
#include <type_traits>
template<class T1, class T2>
using determine = typename std::conditional<
std::is_same<T1,int>::value || std::is_same<T2,int>::value,
int, double>::type;
int main()
{
int a = 3;
double b = 2;
long c = 3L;
using type1 = determine<decltype(a),decltype(b)>;
std::cout << (static_cast<type1>(a) / static_cast<type1>(b)) << std::endl;
using type2 = determine<decltype(b),decltype(c)>;
std::cout << (static_cast<type2>(b) / static_cast<type2>(c)) << std::endl;
}
Upvotes: 7
Reputation: 206627
You can use template metaprogramming techniques to accomplish your goal.
template <typename T1, typename T2> struct TypeSelector
{
using type = double;
};
template <typename T1> struct TypeSelector<T1, int>
{
using type = int;
};
template <typename T2> struct TypeSelector<int, T2>
{
using type = int;
};
template <> struct TypeSelector<int, int>
{
using type = int;
};
and then, use:
int main()
{
int a = 3, b = 2;
using type1 = TypeSelector<decltype(a), decltype(b)>::type;
std::cout << (static_cast<type1>(a) / static_cast<type1>(b)) << std::endl;
float c = 4.5f;
using type2 = TypeSelector<decltype(a), decltype(c)>::type;
std::cout << (static_cast<type2>(a) / static_cast<type2>(c)) << std::endl;
using type3 = TypeSelector<decltype(c), decltype(a)>::type;
std::cout << (static_cast<type3>(c) / static_cast<type3>(a)) << std::endl;
}
Upvotes: 9