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 ]