Is it possible to use template together with operator overload (not using template together with the class, but only with the specific operator such as operator[]) in C++?
Here is an example (of course it can not be compiled pass).
class myClass
{
....
template<int i> auto operator[](std::string) -> std::tuple_element_t<t, types>
{
//some code here
}
...
}
int main()
{
myClass myObject;
....
auto a = myObject["rabbit"]<1>;
auto b = myObject["dog"]<2>;
...
}
Yes, it is possible, but the way you call it has to change. In order to specify the template parameter of operator[] you need to call it with member function syntax like
myObject.operator[]<1>("rabbit");
Which really isn't very nice to look at. Instead, you could just use a named member function like get and then use it like
class myClass
{
....
template<int i> auto get(std::string) -> std::tuple_element_t<t, types>
{
//some code here
}
...
}
auto a = myObject.get<1>("rabbit");
Related
I was wondering if there was any way to do something like this in C++:
$(some typename) func() {
if (/*condition*/) return int;
else return bool;
}
Edit: I think I wasn't clear enough. I was wondering if you could say something like this: func() var = /*something*/. Basically, the function is returning something like int or bool (the type, not a value of that type).
A function cannot choose which type to return at runtime; it must have a definite return type at compile time. What you can use instead is a template with a member type. You will need to use template metaprogramming for this. As an example, here is an adapter template that makes a type T into its const pointer form:
template <typename T>
struct make_const_ptr {
using type = const T*;
};
template <typename>
using make_const_ptr_t<T> = typename make_const_ptr<T>::type
This is essentially a "function" that returns a type, in the sense that you can "call" it by writing make_const_ptr_t<T>. You can do a bunch more operations on types using template metaprogramming: https://www.geeksforgeeks.org/template-metaprogramming-in-c/ .
Plus, you said in your comments that you wanted to use this to choose which template function to use on a polymorphic type. An even better way to do this is to use a virtual function; doing different things for different polymorphic types is the whole point of virtual functions:
template <typename T>
void template_function(const T& obj) {
// ...
}
class Base {
// ...
virtual void do_template_function() = 0;
};
class D : public Base {
virtual void do_template_function() {
template_function<D>(*this);
}
};
class E : public Base {
virtual void do_template_function() {
template_function<E>(*this);
}
};
void f(Base* obj) {
// ...
obj->do_template_function();
// ...
}
There is a cute library Boost.Hana that can be used for computations on types in C++ metaprogramming.
With Boost.Hana a function returning an object representing the C++ type could look like this:
auto func()
{
return hana::if_(
/*constexpr condition*/,
hana::type_c<int>,
hana::type_c<bool>
);
}
Why does the first call not compile?
auto get1 = []<int B>() { return B; };
auto get2 = []<typename B>(B b) { return b; };
int main()
{
get1<5>(); // error: no match for operator<
get2(5); // ok
}
The reason I use this, is an expression repeated many times in code.
Of course I can use a real function template, but just I am curious WHY.
This is easier to understand if you consider what the equivalent class type looks like to your get1:
struct get1_t {
template <int B> operator()() const { return B; }
};
get1_t get1;
get1<5>(); // error
You're trying to provide an explicit template parameter to the call operator, but syntactically you're doing what looks like providing template parameters for get1 itself (i.e. as if get1 were a variable template). In order to provide the template parameter for the call operator, you have to do that directly:
get1.operator()<5>(); // ok
Or restructure the call operator to take something deducible:
template <int B> struct constant { };
get1(constant<5>{});
Or restructure the whole thing to actually be the variable template that it looks like it is:
template <int B>
auto get1 = [] { return B; };
Now, get1<5> is itself a lambda, that you're invoking. That is, rather than a lambda with a call operator template we have a variable template lambda that is itself not a template.
Why does the first call not compile?
auto get1 = []<int B>() { return B; };
auto get2 = []<typename B>(B b) { return b; };
int main()
{
get1<5>(); // error: no match for operator<
get2(5); // ok
}
The reason I use this, is an expression repeated many times in code.
Of course I can use a real function template, but just I am curious WHY.
This is easier to understand if you consider what the equivalent class type looks like to your get1:
struct get1_t {
template <int B> operator()() const { return B; }
};
get1_t get1;
get1<5>(); // error
You're trying to provide an explicit template parameter to the call operator, but syntactically you're doing what looks like providing template parameters for get1 itself (i.e. as if get1 were a variable template). In order to provide the template parameter for the call operator, you have to do that directly:
get1.operator()<5>(); // ok
Or restructure the call operator to take something deducible:
template <int B> struct constant { };
get1(constant<5>{});
Or restructure the whole thing to actually be the variable template that it looks like it is:
template <int B>
auto get1 = [] { return B; };
Now, get1<5> is itself a lambda, that you're invoking. That is, rather than a lambda with a call operator template we have a variable template lambda that is itself not a template.
In C++, can you have a templated operator on a class? Like so:
class MyClass {
public:
template<class T>
T operator()() { /* return some T */ };
}
This actually seems to compile just fine, but the confusion comes in how one would use it:
MyClass c;
int i = c<int>(); // This doesn't work
int i = (int)c(); // Neither does this*
The fact that it compiles at all suggests to me that it's doable, I'm just at a loss for how to use it! Any suggestions, or is this method of use a non-starter?
You need to specify T.
int i = c.operator()<int>();
Unfortunately, you can't use the function call syntax directly in this case.
Edit: Oh, and you're missing public: at the beginning of the class definition.
You're basically right. It is legal to define templated operators, but they can't be called directly with explicit template arguments.
If you have this operator:
template <typename T>
T operator()();
as in your example, it can only be called like this:
int i = c.operator()<int>();
Of course, if the template argument could be deduced from the arguments, you could still call it the normal way:
template <typename T>
T operator()(T value);
c(42); // would call operator()<int>
An alternative could be to make the argument a reference, and store the output there, instead of returning it:
template <typename T>
void operator()(T& value);
So instead of this:
int r = c.operator()<int>();
you could do
int r;
c(r);
Or perhaps you should just define a simple get<T>() function instead of using the operator.
Aren't you thinking of
class Foo {
public:
template<typename T>
operator T() const { return T(42); }
};
Foo foo;
int i = (int) foo; // less evil: static_cast<int>(foo);
live example. This proves you do not need to specify the template argument, despite the claim in the accepted answer.
In C++, can you have a templated operator on a class? Like so:
class MyClass {
public:
template<class T>
T operator()() { /* return some T */ };
}
This actually seems to compile just fine, but the confusion comes in how one would use it:
MyClass c;
int i = c<int>(); // This doesn't work
int i = (int)c(); // Neither does this*
The fact that it compiles at all suggests to me that it's doable, I'm just at a loss for how to use it! Any suggestions, or is this method of use a non-starter?
You need to specify T.
int i = c.operator()<int>();
Unfortunately, you can't use the function call syntax directly in this case.
Edit: Oh, and you're missing public: at the beginning of the class definition.
You're basically right. It is legal to define templated operators, but they can't be called directly with explicit template arguments.
If you have this operator:
template <typename T>
T operator()();
as in your example, it can only be called like this:
int i = c.operator()<int>();
Of course, if the template argument could be deduced from the arguments, you could still call it the normal way:
template <typename T>
T operator()(T value);
c(42); // would call operator()<int>
An alternative could be to make the argument a reference, and store the output there, instead of returning it:
template <typename T>
void operator()(T& value);
So instead of this:
int r = c.operator()<int>();
you could do
int r;
c(r);
Or perhaps you should just define a simple get<T>() function instead of using the operator.
Aren't you thinking of
class Foo {
public:
template<typename T>
operator T() const { return T(42); }
};
Foo foo;
int i = (int) foo; // less evil: static_cast<int>(foo);
live example. This proves you do not need to specify the template argument, despite the claim in the accepted answer.