8

To start, I have something like this:

class Test {
    std::vector<int> a, b;
    void caller(...) { callee(...); }
    void callee(...) { /* Do stuff with 'a' */ }
}

What I wanted is to have a function that does exactly the same as callee but for vector b. To do this there are two obvious solutions:

  • Pass vector a or b as argument. However, callee is a recursive function that can go for hundreds of calls, and passing the vectors as arguments would just be unnecessary overhead.
  • Copy the function callee and use vector b, which would be the best alternative, despite the fact that callee is quite a long function and I would have a lot of duplicate code.

Out of curiosity, I went looking for the templates part and I noticed that can be used for

lvalue reference type

pointer type

pointer to member type

So I tried to do this:

class Test {
    std::vector<int> a, b;
    void caller(...) { callee<a>(...); }
    template <std::vector<int> &x> void callee(...) { /* Do stuff with 'x' */ }
}

but I get

error: use of ‘this’ in a constant expression

Is there any way to achieve this either with a reference or a pointer?

By the way, what I want can be seen as a function-scoped #define

Community
  • 1
  • 1
gmardau
  • 389
  • 2
  • 11
  • "and passing the vectors as arguments would just be unnecessary overhead" I don't believe that you will even notice the "overhead" when you pass the vector by reference or pointer. – Simon Kraemer Nov 03 '16 at 12:41
  • Well, yeah. But I would much rather opt for the second solution and not have any overhead at all. – gmardau Nov 03 '16 at 12:48
  • If you want to use a template like that restrictively, you'd want to do `template ` and wherever you use the type, accept a `T&` - if you specifically need to prevent it being used for say, `std::vector` then use `std::enable_if` for a non-match. – Olipro Nov 03 '16 at 12:50
  • Even I agree that passing as a parameter will not cause overhead question is still really interesting I didn't know we can pass variable as template arguments like that. – Kadir Erdem Demir Nov 03 '16 at 13:08

4 Answers4

9

Arrays and even tuples, but no love for good old pointers-to-members ?

class Test {
    std::vector<int> a, b;

    void caller(/*...*/) { callee<&Test::a>(/*...*/); }

    template <std::vector<int> Test::*vec>
    void callee(/*...*/) { /* Do stuff with `(this->*vec)` */ }
};
Quentin
  • 62,093
  • 7
  • 131
  • 191
4

You cannot use a reference to a data member as a template argument: templates are compile-time, and the value of this is not known until runtime. In other words, you'd need a separate instantiation (separate binary code) for each runtime object of type Test.

What you can do is replace a and b with an array, and templatise callee by index into this array:

class Test {
    std::array<std::vector<int>, 2> ab;
    void caller(...) { callee<0>(...); }
    template <size_t idx>
    void callee(...) { /* Do stuff with 'ab[idx]' */ }
}

This way, you get only two instantiations of callee (one for 0 and one for 1), with the indexing done (or at least doable) at compile time.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
2

Simply use a facade:

class Test {
    std::vector<int> a, b;
    void caller_a(...) { callee(a); }
    void caller_b(...) { callee(b); }
    void callee(std::vector<int> &a_or_b, ...) {
    }
}

callee() will refer to its parameter, which will be passed in as one or the other class member.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
1

In the same logic as @Angew's answer, you could also use std::tuple, and it's quite interesting as with tuple you can also use different kind of containers in your callee function :

class Test {
    std::tuple<std::vector<int>, std::list<int> > ab;
    void caller(...) { callee<0>(...); }
    template <size_t idx>
    void callee(...) { 
    ...
    auto aIt = std::get<idx>(ab).begin(); // gets either the vector or the list depending on template value
    ...
    }
}
Othman Benchekroun
  • 1,998
  • 2
  • 17
  • 36