14

I have something like the following code:

   template<typename T1, typename T2, typename T3, typename T4>
   void inc(T1& t1, T2& t2, T3& t3, T4& t4) { ++t1; ++t2; ++t3; ++t4; }

   template<typename T1, typename T2, typename T3>
   void inc(T1& t1, T2& t2, T3& t3) { ++t1; ++t2; ++t3; }

   template<typename T1, typename T2>
   void inc(T1& t1, T2& t2) { ++t1; ++t2; }

   template<typename T1>
   void inc(T1& t1) { ++t1; }

I'd like to reimplement it using the proposed variadic templates from the upcoming standard. However all the examples I've seen so far online seem to be printf like examples, the difference here seems to be the use of references. I've come up with the following:

inline void inc() { }

template<typename T>
inline void inc(T&& t) { ++t; }

template<typename T,typename ... Args>
inline void inc(T&& t, Args&& ... args) { ++t; inc(args...); }

What I'd like to know is:

  • Should I be using r-values instead of references?

  • Possible hints or clues as to how to accomplish what I want correctly.

  • What guarantees does the new proposed standard provide in regard to the issue of the recursive function calls, is there some indication that the above variadic version will be as optimal as the original? (should I add inline or some-such?)

River
  • 8,585
  • 14
  • 54
  • 67
Hippicoder
  • 1,565
  • 3
  • 17
  • 21
  • 7
    Out of curiosity, I threw those functions at gcc4.5, and the recursion was removed and the whole thing got inlined. – Dennis Zickefoose Apr 11 '10 at 05:59
  • `T&&` is not an rvalue reference because it succeeds a Template Parameter (in this case `T`). Therefore its a Universal Reference/Forwarding Reference that binds to everything. Same is true for `Arg&&`. See [detail](http://en.cppreference.com/w/cpp/language/reference#Forwarding_references). – Jakob Riedle Aug 31 '17 at 08:26

3 Answers3

13

I would not use rvalue references here, because that will allow you to bind to rvalues which can allow such nonsensical code as:

inc(1);

So, I would stick with regular references:

template<typename T>
void inc(T& t) { ++t; }

template<typename T,typename ... Args>
void inc(T& t, Args& ... args) { ++t; inc(args...); }
R Samuel Klatchko
  • 74,869
  • 16
  • 134
  • 187
  • 2
    @Hippicoder: I think you misunderstood R Samuel. He said "I would NOT use rvalue references" – sellibitze Apr 11 '10 at 05:40
  • 1
    @sellibitze: I see, so in short rvalue refs wont add anything special to this code sample - wont make it any faster or slower. – Hippicoder Apr 11 '10 at 05:43
  • 3
    Since a parameter pack can match an empty list, these overloads are ambiguous in the case of one argument. You should use `void inc() {}` as the base case, and probably better to use specialization instead of overloading. – Potatoswatter Apr 11 '10 at 13:20
2

Should I be using r-values instead of references?

You mean rvalue references? No, I see no reason for those.

Possible hints or clues as to how to accomplish what I want correctly.

You're already there. Your code should do what you want.

What guarantees does the new proposed standard provide wrt the issue of the recursive function calls, is there some indication that the above variadic version will be as optimal as the original? (should I add inline or some-such?)

The C++ standard doesn't guarantee any inlining. You could check out what the compiler generates. If you want everything to be inlined -- including the upmost inc-call -- you could put an inline to both of the functions as a request. If you want something like your non-variadic template, you could wrap it like this:

inline void inc_impl() {}

template<typename T, typename...U>
inline void inc_impl(T& t, U&...u) { ++t; inc_impl(u...); }

template<typename...T>
void inc(T&...t) { inc_impl(t...); }

Now inc is not inline while each of its implementations will probably contain no real function calls when the inlining of inc_impl calls are done -- but again, there's no guarantee.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
  • 6
    `inline` isn't really going to have any effect on inlining. – GManNickG Apr 11 '10 at 05:51
  • 1
    @sellibitze: Assuming the standard doesn't make any such guarantees, would it be reasonable to assume competent compilers under normal circumstances will optimize such a piece of code into a series of inline inc's such as what the original overload version does. – Hippicoder Apr 11 '10 at 06:02
  • 3
    @Hippicoder: Yes, I would think so. The only places where the keyword `inline` is typically necessary are non-template functions that are supposed to be used in multiple translation units because inline allows you to put a definition in every TU which makes inlining easier as it doesn't require link-time optimization. – sellibitze Apr 11 '10 at 06:13
1

What guarantees does the new proposed standard provide wrt the issue of the recursive function calls, is there some indication that the above variadic version will be as optimal as the original? (should I add inline or some-such?)

The standard won't guarantee an optimization will be performed, it only specifies the behavior and the result. Whether the function will be inlined problem of the implementation.

In fact, the inline keyword is only a hint to the compiler which is often ignored because the compiler can decide better.

Finally, g++-4.5 completely inlined all inc functions at -O2. Don't know if it's what you want.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 1
    @KennyTM: inlining is pretty much what I was looking for, I was hoping to clarify that major compilers (msvc, intel and gcc) would inline the calls such as the original overloads. – Hippicoder Apr 11 '10 at 06:00