3

Let’s say I have a templated function:

template<typename... Args>
void my_log_function(Args&&... args)
{
    // Work, work, work.
}

And then invoke it as:

my_log_function("1");
my_log_function("22");
my_log_function("333");
my_log_function("4444");
my_log_function("55555");

Since the type of a string literal is const char[n], I believe this will create a lot of instantiations of my_log_function (Visual Studio Intellisense seems to suggest this). Has anyone observed this as a problem? Can it be? Can it be prevented?

2
  • 2
    my_log_function(+"1") deduces Args... to be const char* (using unary +); similarly you could dispatch the work to another template, passing decayed arguments (with the intent of eliminating / inlining the originally invoked function) Commented Nov 20, 2013 at 22:47
  • DyP: Nice trick! But I think it would leave many people baffled... Commented Nov 21, 2013 at 15:42

2 Answers 2

2

Yes, this should create a different instance of class template. They should only differ in the size of the array passed by reference and if they array isn't really used any further as array, the generated code should be identical. As far as I know, VC++ folds identical code. If the type is propagated a lot, it may yield code bloat, however.

One way to prevent the code bloat is to conditionally decay types and forward to another template doing the actual work. I think this should do the trick:

template <typename... Args>
void my_real_log_function(Args&&... args) {
    // real implementation goes here
}

template <typename T>
struct decay_array {
    typedef typename std::condition<
        std::is_array<typename std::remove_reference<T>::type>::value,
        typename std::decay<T>::type,
        T>::type type;
};

template <typename... Args>
void my_log_function(Args&&... args) {
    my_real_log_function(std::forward<typename std::decay_array<Args>::type>(args)...);
}

The original version just using typename std::decay<T>::type doesn't work because it would turn references to non-arrays into values which isn't intended.

Sign up to request clarification or add additional context in comments.

6 Comments

@Potatoswatter: If some of the arguments are large and/or non-copyable arguments, passing them by values my not be intended.
@DyP: yes, you are right: std::decay is too aggressive and removes references, too. Seems, it should only decay when passing in references to arrays making the delegation a bit more interesting.
@Potatoswatter: agreed. I didn't claim it does work. ... and I changed the code to only use std::decay when the argument is an array - I think. Something turning arrays into pointers and then delegating to another function is, however, the general approach. Since this is likely to be a common need it may be worth to investigate how to make this operation easy.
OK… the new code has unbalanced angle brackets and condition instead of conditional
typename std::remove_reference<T>::value should probably be typename std::remove_reference<T>::type>::value to balance the brackets
|
1

The solution is not to implement "work, work, work" in such a function. Sanitize the variable arguments into something more canonical and hand the work to a non-template.

Mind the bloat and remember that the template system is designed to generate inline functions.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.