While attempting to upgrade the following function to C++11
int inline formatText(const char* const fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = std::min(vsnprintf(m_text, m_textSize, fmt, args), m_textSize);
va_end(args);
m_text[len] = '\0'; // assume m_textSize is storage size - sizeof('\0')
return len;
}
Obviously since printf deals with PODs, it's not really a problem for this function to accept its arguments by value.
But I realized I wasn't clear on how to achieve macro-like exact forwarding of the arguments, I realize that by inlining a simple version of this the compiler can eliminate pass-by-value, but I'm not exactly sure which of the three following approaches is technically best:
template<typename... Args>
#if defined(APPROACH_1)
int formatText(const char* const fmt, Args...)
#elif defined(APPROACH_2)
int formatText(const char* const fmt, const Args&...)
#else
int formatText(const char* const fmt, Args&&...)
#endif
{
int len = std::min(snprintf(m_text, m_textSize, fmt, std::forward<Args>(args)...);
m_text[len] = '\0';
return len;
}
Since we're talking printf here, copy-by-value isn't terrible, because I shouldn't be passing it non-pod objects; specifying const ref would certainly help complex objects that I don't want copying but clearly is counter-productive for the normal use cases of printf. I'm plain not sure what the side-effects of approach 3 are.
Everything I've read so far has left me with the impression that 'Args&&...' is likely to be the normal case for variadic templates, but in this case my gut tells me to go for approach #1.