13

Suppose a function template:

template <class T>
void foo(T /* dummy */) {...}

Suppose that foo is called like this:

foo(Widget());

Would a Widget object be constructed in this case?

This post asks a similar question about unused arguments (and arguments for dummy parameters are certainly unused). The replies suggest that, unless the function is called through a function pointer, the unused arguments will be optimized out by the compiler.

However, consider the following text in Section 2.5 of Modern C++ by Alexandrescu:

Now say there is a rule in your application: Objects of type Widget are untouchable legacy code and must take two arguments upon construction, the second being a fixed value such as -1. Your own classes, derived from Widget, don't have this problem.

...

In the absence of partial specialization of functions, the only tool available is, again, overloading. A solution would be to pass a dummy object of type T and rely on overloading:

template <class T, class U>
T* Create(const U& arg, T /* dummy */)
{
   return new T(arg);
}
template <class U>
Widget* Create(const U& arg, Widget /* dummy */)
{
   return new Widget(arg, -1);
}

Such a solution would incur the overhead of constructing an arbitrarily complex object that remains unused.

This suggests that the compilers are not smart enough to avoid the construction of the argument for the dummy parameter...

So, which is correct? If Alexandrescu is correct, then why doesn't this optimization happen?

5
  • Both clang and gcc feel the need to construct the object. coliru.stacked-crooked.com/a/40a50d9448543450 Commented Sep 21, 2015 at 13:11
  • @BaummitAugen Would would be the logic behind this "feeling"? Commented Sep 21, 2015 at 13:13
  • 2
    @BaummitAugen Yes, because construction has side-effects. The only way to check this is with a side-effect free constructed class and the assembly. Commented Sep 21, 2015 at 13:14
  • Explaining that would be an answer. But since there already is one, I need not even try. :) Commented Sep 21, 2015 at 13:15
  • As an aside, I hope they describe template<class T>struct tag{};, then use tag<Widget> and tag<T> overloads as a solution to the above problem. Passing type information around as actual instances is not needed. Commented Sep 21, 2015 at 13:28

2 Answers 2

15

Creating an object can have side effects.

Unless the compiler can prove that no side effects happen, or there is no part of the standard that mandates that a side effect happens, eliminating this creation of an object is not allowed under either the as-if (the compiler can do anything with your code, so long as it behaves as-if they didn't do the change, up to the requirements of the standard) or elision (you merge lifetime of some objects in some cases, even if it doesn't behave as-if you did not merge them) rules.

As an example, suppose Widgets registered their existence with a central location. When the object was created, the count of Widgets in existence would go up by 1 -- making that not happen is illegal under the standard.

Even if there are no side effects, proving there are no side effects requires the compiler gather up all of the code involved in creating the Widget, and analyzing it for "doing nothing in the end". This can vary from hard (link-time optimization of a large amount of code with peculiar "the object will go away at time Y" constraints to determine if any side effects are mandated), to impossible (we are talking about analyzing the non-trivial properties of a Turing complete computation result).

And all of this for a relatively strange corner case, where "someone created an object for no good reason, then discarded it without using it".

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

2 Comments

It could also be easy, e.g. if struct Widget {int x; int y; int widgetID;};
@immibis Yes, it can be; your example isn't an expensive to copy class, so you don't care (addint data[1<<18] to make it still POD and yet expensive). And even there, if the function is defined in one compilation unit and used in another, it requires LTO to eliminate the copy.
11

It's not about being "smart"; it's about being correct. There is no rule in the standard that allows the elision of this construction. It could only be avoided, by the as-if rule, if the constructor has no side-effects.

More generally, it is not always possible for the code at the call site to know whether the parameter is "dummy" at the definition site. Otherwise, the language may have been specified differently, though that's purely speculation.


Let's take a std::string as an example, just for argument's sake (lol):

translation unit 1

// (N.B. no variable name; therefore "dummy")
void foo(std::string)
{}

translation unit 2

#include <string>

void foo(std::string argument);

int main()
{
   foo("will this be used to construct a std::string?");
}

Yes, because as far as TU2's concerned, it must be.


In the converse example, eliding the construction would obviously be a terrible mistake:

translation unit 1

#include <string>
#include <iostream>

void foo(std::string argument)
{
    std::cout << argument << std::endl;
}

translation unit 2

#include <string>

// (N.B. no variable name; therefore "dummy")
void foo(std::string);

int main()
{
   foo("will this be used to construct a std::string?");
}

No (by your "proposal"), causing a right proper mess.


By the nature of C++'s compilation model, both of these scenarios are 100% undetectable.

6 Comments

Of course! How could I overlook this?! The point is side effects! You should emphasize this point in the reply.
Compilers are not limited to optimization over one translation unit (at least in theory, if not in practice). LTO is already here, and compilers are getting better at it.
@Yakk: But it's fundamentally limited. Now consider runtime linking.
@LightnessRacesinOrbit Simple: ship the source (or something equivalent), and recompile both sides. ;) More seriously, where is run-time linking covered in the C++ standard? But yes, in practice, attempting to do this is hard to impossible.
@Yakk: It's not covered in the C++ standard, and I did not claim that it was. Indeed, the entire message of my answer is that the standard makes no accommodation for such an "optimisation", and your only hope is relying on a combination of the as-if rule and whatever psychic powers your modern toolchain can magick up. :)
|

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.