11

I'm reviewing a collegue's code, and I see he has several constants defined in the global scope as:

const string& SomeConstant = "This is some constant text";

Personally, this smells bad to me because the reference is referring to what I'm assuming is an "anonymous" object constructed from the given char array.

Syntactically, it's legal (at least in VC++ 7), and it seems to run, but really I'd rather have him remove the & so there's no ambiguity as to what it's doing.

So, is this TRULY safe and legal and I'm obsessing? Does the temp object being constructed have a guaranteed lifetime? I had always assumed anonymous objects used in this manner were destructed after use...


So my question could also be generalized to anonymous object lifetime. Does the standard dictate the lifetime of an anonymous object? Would it have the same lifetime as any other object in that same scope? Or is it only given the lifetime of the expression?


Also, when doing it as a local, it's obviously scoped differently:

class A
{
    string _str;

public:
    A(const string& str) :
        _str(str)
    {
        cout << "Constructing A(" << _str << ")" << endl;
    }

    ~A()
    {
        cout << "Destructing A(" << _str << ")" << endl;
    }
};

void TestFun()
{
    A("Outer");
    cout << "Hi" << endl;
}

Shows:

Constructing A(Outer); Destructing A(Outer); Hi

Doug T.
  • 64,223
  • 27
  • 138
  • 202
James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • 4
    Can someone also answer why it's preferable to -not- using a reference? – Kenan Banks Jun 22 '09 at 17:40
  • 1
    It isn't. Or rather, the standard basically says that if it's global it has mostly identical semantics. (I.e., the lifetime of a temporary is bound to the lifetime of the const reference it is bound to, or something like that.) – MSN Jun 22 '09 at 18:01
  • however, in that case, you're spending 4 extra bytes aren't you? 4 for the reference and the space for the behind-the-scenes temporary? – James Michael Hare Jun 22 '09 at 18:10
  • Please define what you mean by "anonymous object". That term is not in the standard. – Matthew Flaschen Jun 22 '09 at 18:25
  • 2
    What was his reason for making it a reference? – sean e Jun 22 '09 at 18:32
  • by anonymous, i mean constructing a non-named object, like the first line in TestFun() above. Is there a standard name for that? – James Michael Hare Jun 23 '09 at 14:57
  • I think his reason was that I told him when he passes a string into a fxn he should pass by const-reference to avoid the copy cost, and he did that to his const string& for constants (perhaps a global search-replace gone amuck?) and for return values on local variables (now that IS an error!) – James Michael Hare Jun 23 '09 at 15:21

7 Answers7

14

It's completely legal. It will not be destructed until the program ends.

EDIT: Yes, it's guaranteed:

"All objects which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these objects shall last for the duration of the program (3.6.2, 3.6.3)."

-- 2008 Working Draft, Standard for Programming Language C++, § 3.7.1 p. 63

As Martin noted, this is not the whole answer. The standard draft further notes (§ 12.2, p. 250-1):

"Temporaries of class type are created in various contexts: binding an rvalue to a reference (8.5.3) [...] Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions shall be respected as if the temporary object had been created. [...] Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. [...] There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. [...] The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except as specified below."

I tested in g++ if that makes you feel any better. ;)

Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
  • Is that guaranteed though? I know that it behaves that way in Visual C++, but I want to make sure that according to C++ standard an anonymous object created in that way lives for the lifetime of the program. – James Michael Hare Jun 22 '09 at 17:56
  • so i guess my real question is, from a C++ standard point of view, do anonymous objects created in a scope (global, etc) also get the same scope, or do anonymous objects only live for the expression in which they're used? – James Michael Hare Jun 22 '09 at 17:59
  • but it is anonymous, isn't it? Because it's a refernece, not an object. If it were const string SomeConstant = "blah blah blah"; then it's an object initialization, but if it is: const string& SomeConstant = "blah blah blah"; isn't that instead: const string& SomeConstant = string("blah blah blah"); It seems with the reference there there's an anonymous object in between because the const char * can't be assigned to the string reference, only to a string (through conversion to a string). – James Michael Hare Jun 22 '09 at 18:28
  • but thx for the standard link. I'll look at that. Not trying to pick nits, just want to understand exactly what is happening there and why. – James Michael Hare Jun 22 '09 at 18:28
  • 4
    The section of the standard does not apply. The temporary value created on the RHS of the assignment has static storage duration. It normally would be destroyed when the expression is complete. The only reason it is not destroyed is that it is bound to a const & and thus has it life time extended. – Martin York Jun 22 '09 at 18:53
  • 1
    "it's guaranteed". Yeah, right. It's as guaranteed as the guarantee every compiler vendor gives you that their compiler has no bugs - ever - and is fully, 100%, non-negotiably standard compliant. Drop the &. It's pointless and bad practice. – Roddy Jun 22 '09 at 19:03
  • Roddy, the OP is not asking about compiler bugs (yes, we all know they're numerous). He's asking what the standard provides for. – Matthew Flaschen Jun 22 '09 at 19:27
  • Martin, I've added the section about the rvalue temporary persisting due to the reference. – Matthew Flaschen Jun 22 '09 at 19:36
  • 2
    As a side note: temporaries bound by a reference are destroyed by calling the original destructor of the temporary and not that of the bounded reference. In other words: if you bind a Derived to a Base&, the lifespan of the temporary will be extended to the life of the Base&, and when the reference goes out of scope ~Derived will be called (regardless of whether Base has a virtual destructor) so you can obtain 'virtual destructor' semantics without having a vtable – David Rodríguez - dribeas Jun 22 '09 at 21:59
  • Not directly related to this, but there was a bug in old versions of gcc when temporaries were bound to local statics (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=20416). Personally, although this is legal, I tend to agree that it's not really good style. It also probably takes up more "space" since potentially the implementation of the reference will require storage in addition to the temporary. – Richard Corden Jun 23 '09 at 14:07
11

Yes it is valid and legal.

const string& SomeConstant = "This is some constant text";

// Is equivalent too:

const string& SomeConstant = std::string("This is some constant text");

Thus you are creating a temporary object.
This temporary object is bound to a const& and thus has its lifetime extended to the lifespan of the variable it is bound too (ie longer than the expression in which it was created).

This is guranteed by the standard.

Note:

Though it is legal. I would not use it. The easist solution would be to convert it into a const std::string.

Usage:

In this situation because the variable is in global scope it is valid for the full length of the program. So it can be used as soon as execution enters main() and should not be accessed after executiuon exits main().

Though it technically may be avilable before this your usage of it in constructors/destructors of global objects should be tempered with the known problem of global variable initialization order.

Extra Thoughts:

This on the other hand will not suffer from the problem:

char const* SomeConstant = "This is some constant text";

And can be used at any point. Just a thought.

Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Very true, very true. I typically use const char * const unless I have a need to use the heavier string or if i want to use boost or std string fxns against it. – James Michael Hare Jun 23 '09 at 15:24
6

It might be legal, but still ugly. Leave out the reference !

const string SomeConstant = "This is some constant text";
rtn
  • 127,556
  • 20
  • 111
  • 121
  • 2
    That's what I lean towards as the & buys you nothing, really, in this instance. But if it IS legal and officially so, I hate to knock him for it. – James Michael Hare Jun 22 '09 at 17:57
  • 1
    I wouldn't knock him for it. If it works, I'd leave it be. I think your time would be better spent looking at his design instead. – Rob Jun 22 '09 at 18:16
  • I usually do, i just wanted to make sure it was portable. It happens to work on VC++7, but if it was non-standard and they changed it, it could become a liability. If it is, however, standard compliant, I'm fine with it. – James Michael Hare Jun 22 '09 at 18:19
  • 1
    believe me, I don't want to be a code-nazi, but he has done really silly stuff like returning references to local variables from functions before, so I'm trying to make sure this is legal before i let it slide. – James Michael Hare Jun 22 '09 at 18:21
  • I think the fact that it has caused all this debate should be enough to convince you the code is better off without the reference, even though it is legal. Others seeing it later could have a similar uneasy feeling about it and cause another wave of debate, simply because it's not common. – markh44 Jun 22 '09 at 21:53
  • agreed! If it's not obvious to a person with a reasonable level of experience, it may be best avoided. – James Michael Hare Jun 23 '09 at 15:24
  • Some nice discussion going on here but don't you guys think for an optimized code char pointer will be a better option. Unnecessary call to the constructor can be avoided by using a chat pointer instead of a string object. Just a thought! – sriks Jul 22 '13 at 21:38
4

It's as legal as it's ugly.

Gab Royer
  • 9,587
  • 8
  • 40
  • 58
2

It's legal to extend a temporary variable with a const reference, this is used by Alexandrescu's ScopeGaurd see this excellent explanation by Herb Sutter called A candidate for the "Most important const".

That being said this specific case is an abuse of this feature of C++ and the reference should be removed leaving a plain const string.

Motti
  • 110,860
  • 49
  • 189
  • 262
0

By declaring it as const (which means it can't be changed) and then making it a reference, which implies that someone might change it, seems like bad form, at the very least. Plus, as I am sure you understand, global variables are BAD, and rarely necessary.

xcramps
  • 191
  • 1
  • 2
0

Okay, folks correct me if I'm off the deep end, but here's my conclusions listening to all of your excellent responses:

A) it is syntactically and logically legal, the & extends the lifetime of the temp/anonymous from beyond expression level to the life of the reference. I verified this in VC++7 with:

class A { 
    public: A() { cout << "constructing A" << endl; }
    public: ~A() { cout << "destructing A" << endl; }
};

void Foo()
{
    A();
    cout << "Foo" << endl;
}

void Bar()
{
    const A& someA = A();
    cout << "Bar" << endl;
}

int main()
{
    Foo();    // outputs constructing A, destructing A, Foo
    Bar();    // outputs constructing A, Bar, destructing A
    return 0;
}

B) Though it is legal, it can lead to some confusion as to the actual lifetime and the reference in these cases give you no benefit of declaring it as a non-reference, thus the reference should probably be avoided and may even be extra space. Since there's no benefit to it, it's unnecessary obfuscation.

Thanks for all the answers it was a very interesting dicussion. So the long and short of it: Yes, it's syntactically legal, no it's not technically dangerous as the lifetime is extended, but it adds nothing and may add cost and confusion, so why bother.

Sound right?

Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • Yes, you've got a handle on it now. It's legal, safe, and probably not deliberate obfuscation. But you're right there's no need for the ref. Also, there were some minor errors in this post's code, so I edited it. – Matthew Flaschen Jun 23 '09 at 16:11
  • thanks for the edits, i was typing in a shortened version of the code to keep it brief :-) – James Michael Hare Jun 23 '09 at 17:37