Topic: rvalue-references and overloads


Author: Daniel Frey <news@example.com>
Date: Fri, 19 Mar 2010 18:09:37 CST
Raw View
Hello,

I have a question about rvalue references and overloads. I'm unsure about
whether it's a bug in GCC 4.3+, a DR for the standard or if I am simply
missing something. Consider the following code:

#include <iostream>

template< typename T >
struct A
{
    // default and int ctor
    A() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    explicit A( int ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    // copy ctors
    A( const A& ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
//    A( A& ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
//    A( const A&& ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    A( A&& ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    // conversion ctors from A<U> to A<T>
    template< typename U > A( const A< U >& ) { std::cout <<
__PRETTY_FUNCTION__ << std::endl; }
//    template< typename U > A( A< U >& ) { std::cout << __PRETTY_FUNCTION__
<< std::endl; }
//    template< typename U > A( const A< U >&& ) { std::cout <<
__PRETTY_FUNCTION__ << std::endl; }
    template< typename U > A( A< U >&& ) { std::cout << __PRETTY_FUNCTION__ <<
std::endl; }

    // anything else ctor
    template< typename... Ts > explicit A( Ts&&... ) { std::cout <<
__PRETTY_FUNCTION__ << std::endl; }
};

void p( A< int > )
{
}

#define P( X ) std::cout << "--- p( " #X " );" << std::endl; p( X );

const A< int > f()
{
    return A< int >();
}

int main()
{
    // preparation
    A< int > i;
    const A< int > ci;
    A< double > d;
    const A< double > cd;

    // default and int ctor
    P( A< int >() );
    P( A< int >( 42 ) );

    // copy ctors
    P( i );
    P( ci );
    P( std::move( i ) );
    P( std::move( ci ) );

    // conversion ctors
    P( d );
    P( cd );
    P( std::move( d ) );
    P( std::move( cd ) );

    // anything else ctor
    P( A< int >( 42.0 ) );

    // ?
    P( A< int >( f() ) );
}

The last test-case P( A< int >( f() ) ); is the problem, it invokes the
"anything else ctor" as can be seen by the output of the program. I don't
understand why, I simply think it shouldn't be the case, but I might be wrong.
The only "solution" I found is to enable the four ctors in the above code that
are commented out.

Since I was under the impression that only two overloads by const T& and T&&
should be enough, I wonder if the above program shows a bug in GCC or if the
standard needs to be fixed in some way - or if the above was intended.

Any help to understand the above is appreciated...

Regards, Daniel

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.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: Sat, 20 Mar 2010 14:37:33 CST
Raw View
On 20 Mrz., 01:09, Daniel Frey <n...@example.com> wrote:
> I have a question about rvalue references and overloads. I'm unsure about
> whether it's a bug in GCC 4.3+, a DR for the standard or if I am simply
> missing something. Consider the following code:
>
> #include <iostream>
>
> template< typename T >
> struct A
> {
>     // default and int ctor
>     A() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
>     explicit A( int ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
>
>     // copy ctors
>     A( const A& ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
> //    A( A& ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
> //    A( const A&& ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
>     A( A&& ) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
>
>     // conversion ctors from A<U> to A<T>
>     template< typename U > A( const A< U >& ) { std::cout <<
> __PRETTY_FUNCTION__ << std::endl; }
> //    template< typename U > A( A< U >& ) { std::cout << __PRETTY_FUNCTION__
> << std::endl; }
> //    template< typename U > A( const A< U >&& ) { std::cout <<
> __PRETTY_FUNCTION__ << std::endl; }
>     template< typename U > A( A< U >&& ) { std::cout << __PRETTY_FUNCTION__ <<
> std::endl; }
>
>     // anything else ctor
>     template< typename... Ts > explicit A( Ts&&... ) { std::cout <<
> __PRETTY_FUNCTION__ << std::endl; }
>
> };
>
> void p( A< int > )
> {
> }
>
> #define P( X ) std::cout << "--- p( " #X " );" << std::endl; p( X );
>
> const A< int > f()
> {
>     return A< int >();
>
> }
>
> int main()
> {
>     // preparation
>     A< int > i;
>     const A< int > ci;
>     A< double > d;
>     const A< double > cd;
>
>     // default and int ctor
>     P( A< int >() );
>     P( A< int >( 42 ) );
>
>     // copy ctors
>     P( i );
>     P( ci );
>     P( std::move( i ) );
>     P( std::move( ci ) );
>
>     // conversion ctors
>     P( d );
>     P( cd );
>     P( std::move( d ) );
>     P( std::move( cd ) );
>
>     // anything else ctor
>     P( A< int >( 42.0 ) );
>
>     // ?
>     P( A< int >( f() ) );
> }
>
> The last test-case P( A< int >( f() ) ); is the problem, it invokes the
> "anything else ctor" as can be seen by the output of the program. I don't
> understand why, I simply think it shouldn't be the case, but I might be wrong.
> The only "solution" I found is to enable the four ctors in the above code that
> are commented out.
>
> Since I was under the impression that only two overloads by const T& and T&&
> should be enough, I wonder if the above program shows a bug in GCC or if the
> standard needs to be fixed in some way - or if the above was intended.
>
> Any help to understand the above is appreciated...

The compiler behavior as well as the core wording looks
quite normal/expected to me. Your code example is rather
convoluted, but the essence of your example is a
direct-initialization of A<int> in the expression:

A<int>( f() )

where f() is an rvalue of type const A< int >.

According to 8.5 [dcl.init]/16 b. 6 subbullet 1 the
applicable constructors are enumerated and
overload resolution attempts to find the best one.
According to [over.match.ctor] all explicit and
non-explicit c'tors will be considered.

Among the viable candidates we have:

A( const A& )
template< typename U > A( const A< U >& )
template< typename... Ts > explicit A( Ts&&... )

In particular, the c'tors

A( A&& )
template< typename U > A( A< U >&& )

are not viable, because they cannot bind a
const-qualified rvalue.

The source is an rvalue, therefore c'tors accepting
rvalues will be preferred over those accepting
lvalues. This leads us naturally to

template< typename... Ts > explicit A( Ts&&... )

with Ts deduced as a single parameter of
type const A to instantiate

template<> explicit A(const A&&)

as the best match. If you want to catch this
case, you don't need to uncomment *all*
commented c'tors, instead this one

A( const A&& )

would suffice, which is more specialized
than the "anything else ctor".

HTH & Greetings from Bremen,

Daniel Kr   gler



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