From: Goran on
On Feb 3, 3:49 pm, "Martin B." <0xCDCDC...(a)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! ]

From: Goran on
On Feb 6, 2:20 am, "Martin B." <0xCDCDC...(a)gmx.at> wrote:
> For me the bottom line so far is that due to the semantics of references
> I will try to use them only for temporary/current-callstack stuff and
> never when I need to keep referencing the thing the reference points to.
> I'll try to give an example:
> void fA(T* p) { // or const*
> // 1) work with p here
> // 2) may also keep p to reference the pointee after fA has been left}
>
> void fB(T& r) { // or const&
> // 1) work with r here
> // 2) do NOT keep a reference (pointer) to r after
> // fB has been left since const& references far too
> // easily bind to temporaries
>
> }

Yeah, that's pretty much OK.

I, for example, do use reference members, but only for
1. functor-style classes, when reference is to something that's big to
copy
2. very high-level "context" information that I am reasonably sure
can't possibly go away while code using it is running.

I feel that "2. may also keep p..." part is way too loose, because you
lose the notion of object ownership and lifetime. If I were looking at
such code, I'd always ask myself "so, can I e.g. delete p after a call
to fA or not, then?" The answer to that question is IMO too often
__elsewhere__, not at call site, and not in the function itself.

So I feel that you are looking for an easy way out WRT object
ownership and lifetime. But I frankly see no easy way out in a
language with manual memory management. IMO, that has to be enforced
through correct coding, code documentation, and coding techniques
(e.g, in C++, reference-counted pointers such as boost/
tr1::shared_ptr).

Goran.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]