Re: Why you should never use a const& parameter to initialize a const& member variable!
On Feb 3, 3:49 pm, "Martin B." <0xCDCDC...@gmx.at> wrote:
myFAQ 0815 - Why you should never use a const& parameter to initialize a
const& member variable!
Today, once again, I shot myself in the foot. I thought I'd share this.
Rule: You must never use a const-reference parameter to a constructor to
initialize a const-reference member-variable.
Reason: const& parameters bind to temporaries. You do not want to track
temporaries!
Solution: Use a const* parameter
If you want a const& member variable in a class to reference something,
then it has to be initialized in the ctor. But you must not use a const&
parameter to the ctor to initialize the member, because this parameter
would bind to a temporary and then you would be tracking the temporary
instead of the original value.
Example demonstrating the issue:
--------------------------------
#include <iostream>
using namespace std;
class Bad {
int const& tracker_;
public:
explicit Bad(int const& to_track)
: tracker_(to_track)
{ }
void print() {
cout << "bad tracker_ is: " << tracker_ << endl;
}
};
class Better {
int const& tracker_;
public:
explicit Better(int const* to_track)
: tracker_(*to_track)
{ }
void print() {
cout << "better tracker_ is: " << tracker_ << endl;
}
};
int f() {
static int i = 1;
i += 5;
return i;
}
int main()
{
int t = 100;
char c = 32;
Bad a1( f() ); // compiles: bad
// Better b1( &(f()) ); - compiler error: good
Bad a2( c ); // compiles: bad
// Better b2( &c ); - compiler error: good
Bad a3( t );
Better b3( &t );
t = 166;
c = 64;
t = f();
a1.print(); // May crash or just print 6 (or whatever)
// b1.print();
a2.print(); // May crash or just print 32
// b2.print();
a3.print(); // OK
b3.print(); // OK
return 0;}
Hmmm... I disagree. The problem here is that lifetime of stuff is bad.
"Bad" has a requirement that whatever tracker_ references must outlive
an instance of Bad. That requirement was broken through the use of a
temporary.
In C++, I personally frown upon almost any use of a pointer where
pointer can't be NULL (case here), so I don't like your idea for a
solution. If nothing else, you opened a door for this bug:
Bad b(NULL); // compiles - but bad.
In a way, you reached for a compiler help in a strange way, and for a
problem that is not a "compile-time" one ('cause, obviously, object
lifetime in C++ is the job of a programmer, not the compiler).
And indeed, even when you use a pointer, Bad/Better are still such
that pointed-to object must outlive them, so you solved less than you
intended. You solved only that particular temporary problem.
OK, I propose a vote: how many of us here were bitten by
1. use shown here, and how many have been bitten by
2. other badly sorted object lifetimes?
(me: never 1, several times 2).
So in the end, I think that one should just solve this issue through
correct coding and abstain from seeking compiler help.
BTW, similar to your situation: use of a pointer is a long-standing C-
people complaint to C++, and IMO a (rare ;-) ) valid one: they don't
like absence of "&" when passing stuff by reference, because, just by
reading code at call site, it's not visible that said parameter is a
"reference" parameter. In that vein, I quite like byref and out of C#.
Goran.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]