rtpax
rtpax

Reputation: 1777

Is there a way to make a function return a typename?

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

Answers (3)

skypjack
skypjack

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

WhozCraig
WhozCraig

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

R Sahu
R Sahu

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

Related Questions