16

Inspired by this question. Suppose I have class Lock with a default constructor and in some code I write the following statement:

Lock();

this will have the effect of creating a temporary object of class Lock and immediately destroying it. Of course, creation could have some side effects and that would alter the program behavior, however that looks rather weird.

So my first guess is that such statements while completely valid from the language perspective are highly likely to contain a logical error.

Are there some valid usecases of the above statement? Are there some well-known and popular idioms that include such statements? Why would I want such statements in a correct program?

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • the only thing I can think of is replacing two explicit function calls with a single where both require the same arguments, the second instance would not require the parameters to be passed in (as long as references are saved in the temporary). IMHO, it should be a compiler warning at least... – Nim Jun 29 '11 at 09:49
  • It would seem to me if such a thing were useful, then it could just as well be achieved by a free function, which would be far preferable. – Kerrek SB Jun 29 '11 at 09:58

5 Answers5

14

It doesn't look any weirder than a call to a void function named Lock.

Not that I'm suggesting that you go around thinking of functions as just funny-named constructors (of whatever their return type is), but the syntax is intentionally similar.

I can't for the moment think of a good reason to create but not use a Lock, but:

LockSession(lock);

has the same side-effects as you'd expect from:

acquire_and_then_immediately_release(lock);

As you say, this is very rarely what you want, so it might well look to the casual reader like an error. If for some odd reason it is what you want, and you want to avoid confusing people, you can do:

{
    LockSession session(lock);
    // release immediately
}

or for that matter:

void acquire_and_then_immediately_release(Lock &lock) {
    LockSession(lock);
}

with the same effect and less chance of head-scratching whether that's really what you meant, or whether you've just made the common mistake of forgetting to supply a name and hence holding the lock for less time than you should.

Why would you want to acquire and then immediately release a lock? Probably because you're (ab)using it as a slightly peculiar semaphore. For example you could create a bunch of threads with a lock held, and if the first thing each thread does is this, then none of them will get past it until all the thread creation is done (plus anything else the parent wants the threads to be able to see) and the parent releases the lock. There are probably better examples out there of objects whose construction side-effects are significant.

Moving away from side-effects, another possibility is that if you want to check that a string is valid XML, you could write:

xml::dom::Document(mystring);

expecting an exception for invalid data. Again, the code would be more readable if you called a well-named function that does it. And constructing a DOM tree is not the most efficient way to validate XML. But if a class is what you already have then using it is the simplest thing that works.

I suppose the issue is that the name of a class is rarely descriptive of the side-effects of creating and destroying it. So a lone temporary will tend to look weird for that reason.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
2

In order to get away from dangerous va_list style variadics, I've written code that you call like this:

MyFunc() << a << b << c;

Where MyFunc is implemented as a class with an operator << and ~MyFunc() is responsible for doing the desired work. It's awkward to write the MyFunc class, but once it's done, it works well.

So, yes, the pattern of instantiating a temporary object that only lasts for one line can be useful.

StilesCrisis
  • 15,972
  • 4
  • 39
  • 62
1

Similar syntax of creating a temporary is used many a times in template metaprograms or boost kind of libraries. They actually don't create temporary, but simulate its effect.

e.g. is_base_of

You can see temporary object syntax in the sizeof() trick.

Community
  • 1
  • 1
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • I don't think that involves a temporary - it's checking the size of the *type* of the function... – Nim Jun 29 '11 at 09:59
  • @Nim, I have already mentioned that in **bold**. It simulates the effect of temporary. – iammilind Jun 29 '11 at 10:00
  • this construct is purely a compile time thing, so how can there possibly be a temporary involved at compile time? – Nim Jun 29 '11 at 10:01
  • I am afraid that this is not related. Traits et al typically only have static members, enumerations, and type definitions; as you basically point out yourself (sidenote: there is no **bold** as you claim; edit: oh, you've added it afterwards, even if your claim tells it was there from the beginning), you don't answer the question. There is also no effect-simulation using the sizeof-operator trick. – Sebastian Mach Jun 29 '11 at 10:03
  • I mean: What do you actually mean by effect simulation? – Sebastian Mach Jun 29 '11 at 10:12
0

One use case that I can think right now, is related to tempalte metaprogramming.

As you know, you cannot partially specialise a function, while you can do that with classes. To circumvent this problem, one might implement a function as a class, inside the constructor and partially specialise the whole class as he pleases.

I encountered such problem once recently. I needed a function that would recursively call itself, but I wanted that recurssion to be resolved at compile time. In my case, however, I used a static function inside such class, because often I want it to return something different than void.

Consider the following example:

#include <stdio.h>

template <typename T, int i>
struct megapower {
  megapower(T &val) {
    val*=val;
    {megapower<T,i-1> tmp(val);}
  }
};

template <typename T>
struct megapower<T,1> {
  megapower(T &val) {}
};

int main() {
  int v=2;
  {megapower<int,5> tmp(v);}
  printf("%d\n",v);
  return 0;
}

It will compute 2^2^2^2^2 = 65536.

P.S. As it turns out, you cannot write megapower<int,5>(v). The compiler will think that you are trying to create a new object v of that type.

CygnusX1
  • 20,968
  • 5
  • 65
  • 109
  • 1
    It seems possible, however, to write a wrapper template function that does the temporary instantiation (of partially specialized class) inside the function implementation. And it will be possible to write megapower(v) if megapower become a function :) – user396672 Jun 29 '11 at 11:01
  • That seems a fine way to hide up that temporary object mess. Thanks :) – CygnusX1 Jun 29 '11 at 12:57
-3

Compiler's well within rights to eliminate those variables and ellide constructors with side effects from memory.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Do you mean I can't rely on those side effects? Is there wording for that in the Standard? – sharptooth Jun 29 '11 at 09:57
  • 4
    Not true. Only copy constructors can be elided, and then only in the situations laid out in the standard. Other constructors with side-effects are guaranteed to happen (well, the side-effects are: there's always the "as-if" rule). – Steve Jessop Jun 29 '11 at 09:59