Bug 54506 - Defaulted move constructors and move assignment operators are erroneously defined as deleted
Summary: Defaulted move constructors and move assignment operators are erroneously def...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.7.1
: P3 normal
Target Milestone: 4.7.2
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-09-06 17:50 UTC by Nikolka
Modified: 2012-09-10 14:25 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2012-09-07 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Nikolka 2012-09-06 17:50:11 UTC
g++ rejects the following well-formed code:

    template <class T>
        struct A
    {
        A() {}

        A(A const volatile &&) = delete;
        A &operator =(A const volatile &&) = delete;

        template <class U>
            A(A<U> &&) {}
        template <class U>
            A &operator =(A<U> &&) { return *this; }
    };

    struct B
    {
        A<int> a;
        B() = default;
        B(B &&) = default;
        B &operator =(B &&) = default;
    };

    int main()
    {
        B b = B();
        b = B();
    }
The compiler says that the defaulted move functions in B are deleted, however 12.8/11 and 12.8/23 do not define such functions as deleted.
Comment 1 Jonathan Wakely 2012-09-07 23:16:29 UTC
How are you calling g++? What version are you using? What is the diagnostic you get?
Comment 2 Nikolka 2012-09-08 12:17:24 UTC
(In reply to comment #1)
> How are you calling g++?

/mingw-gcc-4.7.1/bin/g++ test.cpp -std=c++11

> What version are you using?

Target: i686-pc-mingw32
Configured with: ../src/configure --prefix=/c/temp/gcc/dest --with-gmp=/c/temp/gcc/gmp --with-mpfr=/c/temp/gcc/mpfr --with-mpc=/c/temp/gcc/mpc --enable-languages=c,c++ --with-arch=i686 --with-tune=generic --disable-libstdcxx-pch --disable-nls --disable-shared --disable-sjlj-exceptions --disable-win32-registry --enable-checking=release --enable-lto
Thread model: win32
gcc version 4.7.1 (GCC) 

> What is the diagnostic you get?

test.cpp: In function 'int main()':
test.cpp:25:17: error: use of deleted function 'B::B(B&&)'
test.cpp:19:9: note: 'B::B(B&&)' is implicitly deleted because the default definition would be ill-formed:
test.cpp:19:9: error: non-static data member 'B::a' does not have a move constructor or trivial copy constructor
test.cpp:26:15: error: use of deleted function 'B& B::operator=(B&&)'
test.cpp:20:12: note: 'B& B::operator=(B&&)' is implicitly deleted because the default definition would be ill-formed:
test.cpp:20:12: error: non-static data member 'B::a' does not have a move assignment operator or trivial copy assignment operator
Comment 3 Nikolka 2012-09-09 20:55:38 UTC
g++ v4.7.2 20120908 (prerelease) compiles the original example successfully, but it fails to compile the following code:

    template <class T>
        struct A
    {
        A() {}

        A(A const volatile &&) = delete;
        A &operator =(A const volatile &&) = delete;

        template <class U>
            A(A<U> &&) {}
        template <class U>
            A &operator =(A<U> &&) { return *this; }
    };

    struct B
    {
        A<int> a;
        B() = default;
    };

    int main()
    {
        B b = B();
        b = B();
    }

Target: i686-pc-linux-gnu
Configured with: ../configure --prefix=../target --enable-languages=c,c++
Thread model: posix
gcc version 4.7.2 20120908 (prerelease) (GCC) 
COLLECT_GCC_OPTIONS='-v' '-std=c++11' '-shared-libgcc' '-mtune=generic' '-march=pentiumpro'
 ../target/libexec/gcc/i686-pc-linux-gnu/4.7.2/cc1plus -quiet -v -D_GNU_SOURCE test.cpp -quiet -dumpbase test.cpp -mtune=generic -march=pentiumpro -auxbase test -std=c++11 -version -o /tmp/cc0973J0.s
GNU C++ (GCC) version 4.7.2 20120908 (prerelease) (i686-pc-linux-gnu)
	compiled by GNU C version 4.7.2 20120908 (prerelease), GMP version 5.0.2, MPFR version 3.1.0, MPC version 0.8.2

test.cpp: In function ‘int main()’:
test.cpp:23:17: error: use of deleted function ‘B::B(const B&)’
test.cpp:15:12: note: ‘B::B(const B&)’ is implicitly deleted because the default definition would be ill-formed:
test.cpp:15:12: error: use of deleted function ‘constexpr A<int>::A(const A<int>&)’
test.cpp:2:16: note: ‘constexpr A<int>::A(const A<int>&)’ is implicitly declared as deleted because ‘A<int>’ declares a move constructor or move assignment operator
test.cpp:24:15: error: use of deleted function ‘B& B::operator=(const B&)’
test.cpp:15:12: note: ‘B& B::operator=(const B&)’ is implicitly deleted because the default definition would be ill-formed:
test.cpp:15:12: error: use of deleted function ‘A<int>& A<int>::operator=(const A<int>&)’
test.cpp:2:16: note: ‘A<int>& A<int>::operator=(const A<int>&)’ is implicitly declared as deleted because ‘A<int>’ declares a move constructor or move assignment operator
Comment 4 Jonathan Wakely 2012-09-09 21:36:40 UTC
The example can be simplified to 

    struct A
    {
        A() {}

        A(A &&) = delete;
        A &operator =(A &&) = delete;
    };

    struct B
    {
        A a;
        B() = default;
    };

    int main()
    {
        B b = B();
        b = B();
    }

I think this is ill-formed, so G++ is right to reject it.

struct A cannot be moved because its move operations are deleted, and cannot be copied because the implicit-declared copy operations are defined as deleted, see [class.copy]/7.  Therefore struct B cannot be moved or copied either.
Comment 5 Nikolka 2012-09-09 22:42:03 UTC
(In reply to comment #4)

These examples aren't similar. An implicitly defined move constructor performs direct-initialization of non-static data members with the corresponding members of the argument, which is interpreted as xvalue (12.8/15). In every such a direct-initialization all constructors are considered (13.3.1.3), including constructor templates. Template argument for the parameter U can be deduced as int, and the produced specialization of the constructor template will have better match than both copy and move constructors.

Similarly for assignment operators.
 
> struct A cannot be moved because its move operations are deleted

This is not so in my example, and g++ correctly handles the following case:

    template <class T>
        struct A
    {
        A() {}

        A(A const volatile &&) = delete;
        A &operator =(A const volatile &&) = delete;

        template <class U>
            A(A<U> &&) {}
        template <class U>
            A &operator =(A<U> &&) { return *this; }
    };

    int main()
    {
        A<int> a = A<int>(); // OK
        a = A<int>(); // OK
    }
Comment 6 Jonathan Wakely 2012-09-09 22:59:01 UTC
(In reply to comment #5)
> These examples aren't similar.

You're right I reduced it too far, sorry.

I'll confirm this then and CC Jason to look at it.
Comment 7 Jason Merrill 2012-09-10 02:09:26 UTC
(In reply to comment #3)
> g++ v4.7.2 20120908 (prerelease) compiles the original example successfully,
> but it fails to compile the following code:

G++ is following the proposed resolution of DR 1402 here; A<int> does not have a move constructor and it is not trivially copyable, so the B move constructor is not implicitly declared.  This seems like a flaw in the 1402 drafting; the template constructor should count.
Comment 8 Nikolka 2012-09-10 06:26:02 UTC
(In reply to comment #7)
> (In reply to comment #3)
> > g++ v4.7.2 20120908 (prerelease) compiles the original example successfully,
> > but it fails to compile the following code:
> 
> G++ is following the proposed resolution of DR 1402 here; A<int> does not have
> a move constructor

A<int> does have a move constructor, which is instantiated from

    A(A const volatile &&) = delete;

See 12.8/3:

    A non-template constructor for class X is a move constructor if its first parameter is of type X&&, const X&&, volatile X&&, or const volatile X&&, and either there are no other parameters or else all other parameters have default arguments
Comment 9 Jason Merrill 2012-09-10 14:08:36 UTC
Author: jason
Date: Mon Sep 10 14:08:32 2012
New Revision: 191140

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=191140
Log:
	PR c++/54506
	* decl.c (move_signature_fn_p): Split out from move_fn_p.
	* method.c (process_subob_fn): Use it.
	* cp-tree.h: Declare it.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/implicit14.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/cp-tree.h
    trunk/gcc/cp/decl.c
    trunk/gcc/cp/method.c
    trunk/gcc/testsuite/ChangeLog
Comment 10 Jason Merrill 2012-09-10 14:24:27 UTC
Author: jason
Date: Mon Sep 10 14:24:19 2012
New Revision: 191146

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=191146
Log:
	PR c++/54506
	* decl.c (move_signature_fn_p): Split out from move_fn_p.
	* method.c (process_subob_fn): Use it.
	* cp-tree.h: Declare it.

Added:
    branches/gcc-4_7-branch/gcc/testsuite/g++.dg/cpp0x/implicit14.C
Modified:
    branches/gcc-4_7-branch/gcc/cp/ChangeLog
    branches/gcc-4_7-branch/gcc/cp/cp-tree.h
    branches/gcc-4_7-branch/gcc/cp/decl.c
    branches/gcc-4_7-branch/gcc/cp/method.c
    branches/gcc-4_7-branch/gcc/testsuite/ChangeLog
Comment 11 Jason Merrill 2012-09-10 14:25:49 UTC
Fixed for 4.7.2.