Topic: Use of operator T const& to get a reference to self


Author: "James Kanze" <james.kanze@gmail.com>
Date: Wed, 28 Mar 2007 12:20:24 CST
Raw View
Given the following program:

    #include <locale>

    class FG
    {
    public:
        explicit FG( std::locale const& l )
                :  myLoc( &l ) {}

        template< typename F >
                operator F const&() const
        {
            return get< F >( *myLoc, &std::use_facet ) ;
        }

    private:
        std::locale const* myLoc ;

        template< typename F >
        F const& get( std::locale const& l,
                      F const& (*f)( std::locale const& ) ) const
        {
            return (*f)( l ) ;
        }
    } ;


    FG
    getF( std::locale const& l )
    {
        return FG( l ) ;
    }

    void
    f()
    {
        std::ctype< char > const& ct = getF( std::locale() ) ;
    }

G++ (4.1.0) instantates the template operator F const& to get
the const reference needed to call the copy constructor (which
of course fails to compile, since use_facet<FG> is not legal).
Providing a non-template:

    operator FG const&() const { return *this ; }

solves the problem, but is g++ correct here?  I would have
expected the temporary "FG( l )" to bind directly to the const
reference of the (compiler generated) copy constructor.

Or maybe my question is: is this intentional?  Given    8.5.3/5,
"[...]If the initializer expression [...]-- has a class type [it
does] and can be implicitly converted to an lvalue of type "cv3
T3", where "cv1 T1" is reference-compatible with "cv3 T3" [...]
then [...] the reference is bound to the lvalue result of the
conversion [...]"  But it doesn't seem at all natural to have a
user defined conversion called for a copy.

--
James Kanze (GABI Software)            mailto:james.kanze@gmail.com
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "=?iso-8859-1?q?Daniel_Kr=FCgler?=" <daniel.kruegler@googlemail.com>
Date: Thu, 29 Mar 2007 13:52:02 CST
Raw View
James Kanze schrieb:

> Given the following program:
>
>     #include <locale>
>
>     class FG
>     {
>     public:
>         explicit FG( std::locale const& l )
>                 :  myLoc( &l ) {}
>
>         template< typename F >
>                 operator F const&() const
>         {
>             return get< F >( *myLoc, &std::use_facet ) ;
>         }
>
>     private:
>         std::locale const* myLoc ;
>
>         template< typename F >
>         F const& get( std::locale const& l,
>                       F const& (*f)( std::locale const& ) ) const
>         {
>             return (*f)( l ) ;
>         }
>     } ;
>
>
>     FG
>     getF( std::locale const& l )
>     {
>         return FG( l ) ;
>     }
>
>     void
>     f()
>     {
>         std::ctype< char > const& ct = getF( std::locale() ) ;
>     }
>
> G++ (4.1.0) instantates the template operator F const& to get
> the const reference needed to call the copy constructor (which
> of course fails to compile, since use_facet<FG> is not legal).
> Providing a non-template:
>
>     operator FG const&() const { return *this ; }
>
> solves the problem, but is g++ correct here?  I would have
> expected the temporary "FG( l )" to bind directly to the const
> reference of the (compiler generated) copy constructor.
>
> Or maybe my question is: is this intentional?  Given    8.5.3/5,
> "[...]If the initializer expression [...]-- has a class type [it
> does] and can be implicitly converted to an lvalue of type "cv3
> T3", where "cv1 T1" is reference-compatible with "cv3 T3" [...]
> then [...] the reference is bound to the lvalue result of the
> conversion [...]"  But it doesn't seem at all natural to have a
> user defined conversion called for a copy.

An interesting question, but IMO the standard is quite clear
here, because 12.3.2/1 says unambigiously:

"[..] A conversion function is never used to convert a (possibly
cv-qualified) object to the (possibly cv-qualified) same object
type (or a reference to it),[..]"

This sentence exists since the 1998 standard and has also
not changed in the current draft. I considered for a while that
gcc might not view a temporary as an object, but this seems
also rather clearly expressed in both 1.8 and 12.2.

I observed that even my quite old mingw gcc 3.4, a rather old
Borland Compiler (I just can't find its version number) and
MS-VS2003 show the unexpected behaviour you mention.
But no newer compiler I tested (including all public available
Comeau online versions and MS-VS2005-SP1) did. Of course
we have no majoritarian decision here, but I tend to say that
the standard is unusually unmistakable here ;-)

All newer compilers follow rather strict the mentioned rule:
To enforce this, I simply added the operator FG(FG&) to your
class and none (except the older ones) tried the conversion
operator but gave up with something like "class "FG" has no
suitable copy constructor".

As a not very relevant sidenode: Even by using the language
extension mode of MS-VS2005 the compiler did not do that
but selected instead the actually forbidden FG(FG&) c'tor with
a corresponding warning

"nonstandard extension used : 'argument' : conversion from 'FG'
to 'FG &' A non-const reference may only be bound to an lvalue;
copy constructor takes a reference to non-const") warning).

Concluding, I would say that a bug report for gcc seems a
reasonable choice.

Greetings from Bremen,

Daniel


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "James Kanze" <james.kanze@gmail.com>
Date: Fri, 30 Mar 2007 11:25:09 CST
Raw View
On Mar 29, 9:52 pm, "Daniel Kr   gler" <daniel.krueg...@googlemail.com>
wrote:
> James Kanze schrieb:

> > Given the following program:

> >     #include <locale>

> >     class FG
> >     {
> >     public:
> >         explicit FG( std::locale const& l )
> >                 :  myLoc( &l ) {}

> >         template< typename F >
> >                 operator F const&() const
> >         {
> >             return get< F >( *myLoc, &std::use_facet ) ;
> >         }

> >     private:
> >         std::locale const* myLoc ;

> >         template< typename F >
> >         F const& get( std::locale const& l,
> >                       F const& (*f)( std::locale const& ) ) const
> >         {
> >             return (*f)( l ) ;
> >         }
> >     } ;

> >     FG
> >     getF( std::locale const& l )
> >     {
> >         return FG( l ) ;
> >     }

> >     void
> >     f()
> >     {
> >         std::ctype< char > const& ct = getF( std::locale() ) ;
> >     }

> > G++ (4.1.0) instantates the template operator F const& to get
> > the const reference needed to call the copy constructor (which
> > of course fails to compile, since use_facet<FG> is not legal).
> > Providing a non-template:

> >     operator FG const&() const { return *this ; }

> > solves the problem, but is g++ correct here?  I would have
> > expected the temporary "FG( l )" to bind directly to the const
> > reference of the (compiler generated) copy constructor.

> > Or maybe my question is: is this intentional?  Given    8.5.3/5,
> > "[...]If the initializer expression [...]-- has a class type [it
> > does] and can be implicitly converted to an lvalue of type "cv3
> > T3", where "cv1 T1" is reference-compatible with "cv3 T3" [...]
> > then [...] the reference is bound to the lvalue result of the
> > conversion [...]"  But it doesn't seem at all natural to have a
> > user defined conversion called for a copy.

> An interesting question, but IMO the standard is quite clear
> here, because 12.3.2/1 says unambigiously:

> "[..] A conversion function is never used to convert a (possibly
> cv-qualified) object to the (possibly cv-qualified) same object
> type (or a reference to it),[..]"

OK.  That's the sentence I was looking for.  Unless there's
something I've misunderstood, I'm copying FG, and the conversion
operator was being used to initialize the FG const& parameter of
the (implicitly declared and defined) copy constructor.

> This sentence exists since the 1998 standard and has also
> not changed in the current draft. I considered for a while that
> gcc might not view a temporary as an object, but this seems
> also rather clearly expressed in both 1.8 and 12.2.

> I observed that even my quite old mingw gcc 3.4, a rather old
> Borland Compiler (I just can't find its version number) and
> MS-VS2003 show the unexpected behaviour you mention.
> But no newer compiler I tested (including all public available
> Comeau online versions and MS-VS2005-SP1) did. Of course
> we have no majoritarian decision here, but I tend to say that
> the standard is unusually unmistakable here ;-)

The sentence you quoted is exceptionally clear.  To discharge
the compiler vendors some, however: this could have been
mentionned in the the section on binding to a reference.  If I
were writing a compiler, that's the section I'd be following in
detail when writing the relevant code, and I'd probably have
missed this one particular case as well.

> All newer compilers follow rather strict the mentioned rule:
> To enforce this, I simply added the operator FG(FG&) to your
> class and none (except the older ones) tried the conversion
> operator but gave up with something like "class "FG" has no
> suitable copy constructor".

> As a not very relevant sidenode: Even by using the language
> extension mode of MS-VS2005 the compiler did not do that
> but selected instead the actually forbidden FG(FG&) c'tor with
> a corresponding warning

> "nonstandard extension used : 'argument' : conversion from 'FG'
> to 'FG &' A non-const reference may only be bound to an lvalue;
> copy constructor takes a reference to non-const") warning).

A reasonable choice, given that earlier versions didn't even
warn when binding a temporary to a non-const reference.  (Sun CC
uses the word "anachronism" for this sort of thing.)

> Concluding, I would say that a bug report for gcc seems a
> reasonable choice.

Sounds right to me.  (To be fair, I should probably install the
latest version, and try it first.  But unless there's already a
bug report on it, I'd be surprised if the behavior changed.)

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]