http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58162

            Bug ID: 58162
           Summary: [C++11] bogus error: use of deleted function
                    'constexpr A::A(const A&)'
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ppluzhnikov at google dot com

Google ref b/10272620

/// --- cut ---
struct A {
 A();
 A(A&&);
};

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

B b;
/// --- cut ---

Using trunk: g++ (GCC) 4.9.0 20130814 (experimental)

g++ -c -std=c++11 t.cc
t.cc: In constructor ‘constexpr B::B()’:
t.cc:6:8: error: use of deleted function ‘constexpr A::A(const A&)’
 struct B {
        ^
t.cc:1:8: note: ‘constexpr A::A(const A&)’ is implicitly declared as deleted
because ‘A’ declares a move constructor or move assignment operator
 struct A {
        ^
t.cc: At global scope:
t.cc:10:3: note: synthesized method ‘constexpr B::B()’ first required here 
 B b;
   ^


Analysys by jdennett

  This is under 8.5p17 (via other sections... 9.2[Class Members]p4 leads
  to 12.6.2, and 12.6.2p8 says "if the entity is a non-static data member
  that has a brace-or-equal-initializer, the entity is initialized as
  specified in 8.5"), and (I believe) should be the same initialization
  as for a local variable declared as

    A const a = A();

  (All references above are in the C++14 CD.)

  8.5p17 says:

  "If the destination type is a (possibly cv-qualified) class type:
  — If the initialization is direct-initialization, or if it is
  copy-initialization where the cv-unqualified version of the source type
  is the same class as, or a derived class of, the class of the destination,
  constructors are considered."

  We're in the case of copy-initialization where the cv-unqualified
  version of the source type is A, and the destination type is "const A",
  but arguably "the class of the destination" is A (because const A isn't
  a class).

  So I believe that this should fall into the same case as
  direct-initialization, overload resolution should pick the A::A(A&&)
  move constructor, and all should be good.

  Wild guesses about how this might go wrong in the compiler:

  It might be that G++ is checking whether the cv-unqualified version of
  the source type is the same as the destination type (rather than the same
  as the cv-unqualified version of the destination type, which is the class
  here).  If so, it would fall into the main case of copy-initialization,
  which creates a temporary of the target type.  That should still work,
  but if the temporary were (incorrectly) considered to be a "const A"
  rather than an "A" then initializing from it would attempt to use the
  (deleted) copy constructor.

  Seeing if changing A(A&&) to A(A const&&) changes anything might be
  informative, or having both overloads present.


Changing 'A(A&&);' to 'A(const AA&&);' or adding it as overload does fix
the compilation problem.

Further patch from jdennett:

With the following hack, the testcase compiles.

$ svn diff gcc
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c     (revision 201651)
+++ gcc/cp/parser.c     (working copy)
@@ -23002,7 +23002,10 @@
         if (BRACE_ENCLOSED_INITIALIZER_P (parsed_arg)
             && CONSTRUCTOR_IS_DIRECT_INIT (parsed_arg))
           flags = LOOKUP_NORMAL;
-         parsed_arg = digest_init_flags (TREE_TYPE (decl), parsed_arg, flags);
+         // TODO(jdennett): Something better than this, and work out how
+         // to test it.
+         parsed_arg = digest_init_flags (TYPE_MAIN_VARIANT (TREE_TYPE (decl)),
+                                         parsed_arg, flags);
       }
    }

Reply via email to