4

I was writing a simple type based dispatcher using unnamed parameter, which is a pretty normal thing I guess.

When it came to actually call the function, I wanted to pick up the overload with out having any variable at hand. Is it possible?

E.g.

void f1(int /*can change the type*/) {
}
int main(int, char*) {
  f1(/*what to put here?*/); 
  return 0;
}

My "real" example was a simple type-based dispatcher. Best what I came up with was a pointer as the unnamed parameter, I think it has least overhead possible:

#include <iostream>
using namespace std;

template<typename U>
class A {
public:

A(const U& u) : u(u) {};

template <typename T>
T get_as() {
    T* t; // <- ugly
    return get(t);
}

U get(U*) {
    return u;   
}

int get(int*) {
    return i;
}

double get(double*) {
    return d;
}

private:
    U u;
    int i = 5;
    double d = 3.14;
};

int main() {
    A<string> a("name");
    cout << a.get_as<double>() << '\n';
    cout << a.get_as<int>() << '\n';
    cout << a.get_as<string>() << '\n';
    return 0;
}

I guess it's purely aesthetic with a pointer, because it's probably optimized, but still I wonder. I also guess this might be impossible, because (AFAIK) this argument goes through stack. So technically there is always this "overhead". Is this right?

Not sure if this should be another question, but is there a way to express, "just pick up this overload" with out trickery?

PS. I figured out I could use T*(0) in the "ugly" line, but it's probably even less readable.

luk32
  • 15,812
  • 38
  • 62

2 Answers2

6

You can't get any clearer or more efficient than passing an empty tag struct:

template<class T> struct tag {};

double get(tag<double>) {
    return d;
}

template <typename T>
T get_as() {
    return get(tag<T>{});
}

An advantage over using pointer types for dispatch is that there's no danger of accidentally indirecting the passed-in null pointer.

(Aside: What happens to empty classes in the AMD64 ABI? - but your calls will usually be inlined.)

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Ha! Looks awesome. Do you know if there is anything in standard for this? There is plenty helper struct templates. So maybe there is no need to define `tag`. – luk32 Mar 24 '16 at 00:13
  • @luk2 I'm afraid not; the standard has some tags but they're concrete (http://en.cppreference.com/w/cpp/iterator/iterator_tags) while the type metafunctions could be used as tags but they really aren't designed for that purpose. An advantage of using your own tag class template is that you can have it serve double duty as an ADL parameter, so that you don't have to qualify free function calls. – ecatmur Mar 24 '16 at 01:31
1

Since you are talking about "aesthetics", I'm wondering if this may help you:

Live Demo

template<typename U>
class A
{
public:
    static_assert(!std::is_same<int, U>::value && !std::is_same<double,U>::value,
                               "Template argument cannot be <int> or <double>.");
    A(const U& u) : u(u) {};
    explicit operator U() { return u; }
    explicit operator int() { return i; }
    explicit operator double() { return d; }

private:
    U u;
    int i = 5;
    double d = 3.14;
};

int main()
{
    A<string> a("wow");
    cout << double(a) << '\n';
    cout << int(a) << '\n';
    cout << string(a) << '\n';
    return 0;
}

I think user-defined conversion operators are not evil if they are explicit.

You cant use them by "mistake". Also, this class should have no overhead.

Jts
  • 3,447
  • 1
  • 11
  • 14
  • This is nice, but the use-case is slightly different. I want a generic way to return a member. It's slightly different semantically, although close. With the conversion you loose the original object, the problem was that this was passed to a generic function, which transformed the concrete member, or something like that. It wasn't mine problem originally. There was an overloaded `set`, which was trivial, and there was a desire for an overloaded `get`. Maybe `get_as` is a bit misleading. Something like `get_me` would be better, like `get_me`, `get_me`. Props for the `static_assert` – luk32 Mar 24 '16 at 01:59