1

So I have a class:

struct C {
  int x[10];

  int const& get(int i) const { return x[i]; }

  template<typename... Ts>
  auto& get(Ts... args) {
    int const& (C::*f)(Ts...) const = &C::get;
    return const_cast<int&>((this->*f)(args...));
  }
};

and the template will generate a non-const getter on demand, so this becomes legal:

int& test(C& c) {
  int i = 4;
  int& r = c.get(i);
  return r;
}

but if I change the original getter's argument type to a reference:

  int const& get(int const& i) const { return x[i]; }

the non-const getter template can't match the const getter anymore.

Obviously it's no big deal passing an int by value, and it's no big deal passing the argument list explicitly to the template, but I have more complex arguments and numerous overloads, and I'd like to infer all my non-const getters with a minimum of copy-paste.

Is there any way past this?


One thought I had was that I could declare the problem argument list within the class but leave the definition to the template, so that the argument matching would have some guidance. I tried adding the following to the class (either before or after the template):

  int& get(int const& i);

Unfortunately this bypasses the template altogether and results in a link error. Adding inline or template to the declaration didn't seem to do the trick either.

sh1
  • 4,324
  • 17
  • 30
  • By "numerous overloads" it turns out I actually have two, but it's the _principle_ I care about. I might get more later. – sh1 Dec 15 '16 at 04:45
  • I think your compiler should have complained about `int& i = c.get(i)` as well since `int&` is not const. – AndersK Dec 15 '16 at 04:55
  • @Anders K., that's why the template exists. It creates a non-const getter by calling the const getter and stripping const from the result. This is useful for using the same getter from various const and non-const methods. If many overloads exist then it picks the one with the matching argument list to the call. Unfortunately "matching" fails if the const getter takes a reference argument, because the call looks like a by-value call. – sh1 Dec 15 '16 at 05:07
  • @AndersK., edited for clarity. – sh1 Dec 15 '16 at 05:30
  • `int const& (C::*f)(std::add_const_t&...) const = &C::get;` is this what you want ? – Arunmu Dec 15 '16 at 05:51
  • 1
    Why do you need `f` the intermediate variable? Why not call `get` directly? – n. m. could be an AI Dec 15 '16 at 06:10
  • @n.m., you're right, I don't need to in this version. I oversimplified my reduced test case. I'd experimented with making it more terse for multiple getters by taking a function pointer argument and calling through that. – sh1 Dec 15 '16 at 07:04
  • I've made a [second attempt](http://stackoverflow.com/q/41160125/2417578) at asking the question I _meant_ to ask. – sh1 Dec 15 '16 at 09:04

1 Answers1

2

Why do you enforce the call to a const member function through an auxiliary pointer to member-function? In such cases I use const qualification of this:

template<typename... Ts>
auto& get(Ts... args) {
    const C* const const_this = this;
    return const_cast<int&>(const_this->get(args...));
}
Leon
  • 31,443
  • 4
  • 72
  • 97
  • Oh, oops! Because it had evolved from another template where I was trying to get an implicit match on the return type. – sh1 Dec 15 '16 at 06:46
  • That does make the code work (so it's answered my question); but I'm not so clear on why. An alternate version, which takes a function pointer argument so that most of the verbiage can be applied to various getters, is still affected. – sh1 Dec 15 '16 at 07:00