Reputation: 129
I'm new to using templates and I want to make a function that returns generic types. I made a very simple template as such
enum tag { SHORT, INT, FLOAT, DOUBLE };
template <typename T>
T get_val(tag t){
switch (t) {
case SHORT: return .. //returns a short;
case INT: return .. //returns a int;
case FLOAT: return .. //returns a float;
case DOUBLE: return .. //returns a double;
}
}
the problem is that floats and doubles are returned as integer numbers. so I wonder why this happens and if there's a way I can use it properly.
Upvotes: 1
Views: 2677
Reputation: 14614
I would recommend doing this with a variant and runtime checks for the type among a limited subset of possible types:
Use a public interface taking a variant with an enum for the correct type (this is a naive version, you should really use std::variant
and get_if
):
enum variant_type
{
variant_null,
variant_int,
variant_float,
variant_double,
}
class variant
{
public:
// Add constructors with the correct type for the
// underlying storage
variant() = default;
variant(int x);
variant(float x);
variant(double x);
// ensure you can check for empty variants and check the type
explicit operator int() const;
explicit operator float() const;
explicit operator double() const;
explicit operator bool() const;
variant_type type() const;
private:
variant_type type_ = variant_null;
void* data_ = nullptr;
};
Then, add many custom function or method overloads that allow you to choose the correct method at runtime:
class my_handler
{
public:
my_handler(const variant& v)
{
switch (v.type()) {
case variant_null;
open(nullptr); break;
case variant_int;
open(int(v)); break;
case variant_float;
open(float(v)); break;
case variant_double;
open(double(v)); break;
}
}
private:
void open(std::nullptr_t nullp);
void open(int x);
void open(float x);
void open(double x);
};
This allows you to have a public interface that is generic for a limited subset of types, but that allows you to avoid having to know the correct type at compile-time.
This can all be done much more idiomatically using std::variant
and get_if
rather than writing your own custom variant class. I just this as an (inefficient) example of how many libraries (like the Windows API and Qt) have implemented their own.
Upvotes: 2
Reputation: 206577
You may not use a different return type using a switch
statement like that.
However, if the input to get_val
is known at compile time, you can use template metaprogramming techniques to get what you need.
enum tag { SHORT, INT, FLOAT, DOUBLE };
template <tag t> struct return_type_chooser;
template <> struct return_type_chooser<SHORT>
{
using type = short;
};
template <> struct return_type_chooser<INT>
{
using type = int;
};
template <> struct return_type_chooser<FLOAT>
{
using type = float;
};
template <> struct return_type_chooser<DOUBLE>
{
using type = double;
};
template <tag t>
typename return_type_chooser<T>::type get_val()
{
...
}
You can use:
get_val<SHORT>()
but not
get_val(SHORT)
Update, in response to OP's comment
If the input to get_val
is known only at run time, std::any
(C++17) or boost::any
should work as the return type.
Upvotes: 4