I'm not sure if it's possible but take this snippet of code:
#include <stdio.h>
struct car {
float speed;
};
struct bike {
float speed;
};
void drive(float *speed)
{
printf("driving at speed %f\n", *speed);
}
template<typename T>
void drive(T driveable)
{
drive(&driveable->speed);
}
int main() {
struct car car = { .speed = 10.f };
struct bike bike = { .speed = 20.f };
drive(&car);
drive(&bike);
}
The idea is that any object that has a member field of type float
and that is named speed
can be used transparently with the function drive
, thanks to the templated overload of drive
which passes the member of the template type to the actual drive
function.
Is it possible to combine the templated function with the non-templated function in a single one? Something along the lines of:
template<typename T>
void drive(T driveable)
void drive(float *speed = &driveable->speed)
{
printf("driving at speed %f\n", *speed);
}
Additionally, would there be another, more elegant way to do what I want?
CodePudding user response:
There is no syntax similar to the one you are proposing. Having two seperate overloads as two seperate overloads is "elegant" enough. Your code is fine as is.
Though, if you want, there is a way to have a single function template that can cover both cases:
#include <cstdio>
#include <type_traits>
struct car {
float speed;
};
struct bike {
float speed;
};
template<typename T>
void drive(const T& t)
{
if constexpr (std::is_same_v<float,T>) {
printf("driving at speed %f\n",t);
} else {
drive(t.speed);
}
}
int main() {
struct car car = { .speed = 10.f };
struct bike bike = { .speed = 20.f };
drive(car);
drive(bike);
drive(0.5f);
}
Whether this or your code is more elegant is largely a matter of opinions.
PS: There is quite a bit of pointless use of pointers in your code. I removed that. Also in C you should use the C header called cstdio
rather than the C header stdio.h
.
CodePudding user response:
Is it possible to combine the templated function with the non-templated function in a single one?
Yes, it is possible. On the other hand, having two overloads in your case is more readable.
If you insist to do so, using concepts (since c 20) and compile time branching using if constexpr
(since c 17) you may do:
#include <concepts> // std::floating_point
// concept for checking whether T has "speed" member which is floating point!
template<typename T>
concept has_float_member = requires (T t) { // nested requirements
requires std::floating_point<decltype(t.speed)>;
};
void drive(auto const& driveable)
{
if constexpr (has_float_member<decltype(driveable)>)
std::cout << driveable.speed << "\n";
else
std::cout << "driving at speed: " << driveable;
}
Now you can call the same function for car
, bike
, and float
s as follows.
drive(car); // pass car and bike like
drive(bike);
drive(2.f);
If you required to pass the references to the instances, adjust the function accordingly.