Is 1-arg overload of variadic template function useful? Should it used a magic && reference? Is Copy-Elision taking place in this case?

52 views Asked by At

I have an existing std::ostream-like type, which has several operator<< overloads for various types. Since that type is used as a const& argument to other APIs, I'm adding convenience factory-functions for easy inline use, at call sites.

I wrote the code below, which works fine (VS2019 and GCC9), but I was wondering several things:

  1. Do I really need the 1-arg overload? Is there benefits to having it?
  2. The 1-arg overload uses (Scott Meyers') magic-references, but should the variadic overload use them to? I tried Ts...&&, but VS2019 didn't like that.
  3. In Debug, I followed the return statements, and saw osl being move-constructed, when I was expecting Copy-Elision to take place, for such a helper, to make it zero-overhead. Is this a Debug-build artifact? Or somehow Copy-Elision cannot take place here? If so, why?
template <typename T> inline
OStreamLike bind(T&& val) {
    OStreamLike osl(1);
    osl << val;
    return osl;
}

template <typename... Ts> inline
OStreamLike bind(Ts... vals) {
    OStreamLike osl(sizeof...(Ts));
    ((osl << vals), ...);
    return osl;
}
1

There are 1 answers

4
Jarod42 On

Do I really need the 1-arg overload? Is there benefits to having it?

1rst overload isn't required.

The only benefit is the reference to avoid copy (which might be done for the variadic overload).

The 1-arg overload uses (Scott Meyers') magic-references, but should the variadic overload use them to? I tried Ts...&&

Forwarding-reference for variadic would use Ts&&... args syntax.

but from the above code, you don't need forwarding references, regular const reference would be my choice const Ts&... args.

In Debug, I followed the return statements, and saw osl being move-constructed, when I was expecting Copy-Elision to take place, for such a helper, to make it zero-overhead. Is this a Debug-build artifact? Or somehow Copy-Elision cannot take place here? If so, why?

NRVO is not mandatory, even in C++17.

NRVO is applicable here, and I expect it at least in optimized build.