1

I am 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 template-overload function drive, which passes the member of the template type to the actual drive free-function.

Is it possible, to combine the template - function with the free-function in a single one? Something along the lines:

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?

JeJo
  • 30,635
  • 6
  • 49
  • 88
aganm
  • 1,245
  • 1
  • 11
  • 30
  • 1
    There is no need to combine them. No need for micro-optimizations. – Jason Oct 28 '22 at 11:13
  • 1
    Why not just write `printf("driving at speed %f\n", driveable->speed)`? – Nelfeal Oct 28 '22 at 11:18
  • @JasonLiam Well, the goal isn't to achieve micro-optimizations, but rather just to make the API more concise. – aganm Oct 28 '22 at 11:38
  • @Nelfeal Because driveable may contain other fields which I don't want my function to have access to, unless explicitly stated. – aganm Oct 28 '22 at 11:46
  • @aganm But your function template does have access to those other fields. And the functions in both answers (as of yet) also do. It sounds like you want an interface, as in an object-oriented abstract class `drivable` that `car` and `bike` inherit from. AFAIK that's the only way to achieve exactly what you want, apart from just calling `drive` with the speed directly. Personally, I would just *not access* those other fields, because that's my function and my types and I do what I want with them. – Nelfeal Oct 28 '22 at 12:04

2 Answers2

3

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);
}

Live Demo

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.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
3

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 on doing so, using concepts (since ) and compile time branching using if constexpr (since ) 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)>)
        drive(driveable.speed);
    else // optional: if constexpr (std::floating_point<std::decay_t<decltype(driveable)>>)
        std::printf("Driving at speed: %f\n", driveable);
}

See a demo in godbolt.org

Now you can call the same function for car, bike, and floats 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.

JeJo
  • 30,635
  • 6
  • 49
  • 88