P0512 corrected the specification of C++17 class template argument deduction to work more like constructor overload resolution in initialization; in particular, this means that we should do the same two-phase overload resolution for a class with a list constructor. This patch implements that, so that e.g.
#include <vector> int a[1]; std::vector v{a, a, std::allocator<int>()}; will now work. Tested x86_64-pc-linux-gnu, applying to trunk.
commit 7cede2ad071dbd7ab1f174465305e5568b5d311a Author: Jason Merrill <ja...@redhat.com> Date: Wed Jul 12 12:53:23 2017 -0400 P0512R0 - Deduction from an initializer list. * pt.c (do_class_deduction): Do list deduction in two phases. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index bd02951..0df6854 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -25329,14 +25329,20 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, tree type = TREE_TYPE (tmpl); + bool try_list_ctor = false; + vec<tree,va_gc> *args; if (init == NULL_TREE || TREE_CODE (init) == TREE_LIST) args = make_tree_vector_from_list (init); - else if (BRACE_ENCLOSED_INITIALIZER_P (init) - && !TYPE_HAS_LIST_CTOR (type) - && !is_std_init_list (type)) - args = make_tree_vector_from_ctor (init); + else if (BRACE_ENCLOSED_INITIALIZER_P (init)) + { + try_list_ctor = TYPE_HAS_LIST_CTOR (type); + if (try_list_ctor || is_std_init_list (type)) + args = make_tree_vector_single (init); + else + args = make_tree_vector_from_ctor (init); + } else args = make_tree_vector_single (init); @@ -25391,13 +25397,43 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, saw_ctor = true; } - if (args->length () < 2) + tree call = error_mark_node; + + /* If this is list-initialization and the class has a list constructor, first + try deducing from the list as a single argument, as [over.match.list]. */ + tree list_cands = NULL_TREE; + if (try_list_ctor && cands) + for (lkp_iterator iter (cands); iter; ++iter) + { + tree dg = *iter; + if (is_list_ctor (dg)) + list_cands = lookup_add (dg, list_cands); + } + if (list_cands) + { + ++cp_unevaluated_operand; + call = build_new_function_call (list_cands, &args, tf_decltype); + --cp_unevaluated_operand; + + if (call == error_mark_node) + { + /* That didn't work, now try treating the list as a sequence of + arguments. */ + release_tree_vector (args); + args = make_tree_vector_from_ctor (init); + } + } + + /* Maybe generate an implicit deduction guide. */ + if (call == error_mark_node && args->length () < 2) { tree gtype = NULL_TREE; if (args->length () == 1) + /* Generate a copy guide. */ gtype = build_reference_type (type); else if (!saw_ctor) + /* Generate a default guide. */ gtype = type; if (gtype) @@ -25419,22 +25455,29 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, return error_mark_node; } - ++cp_unevaluated_operand; - tree t = build_new_function_call (cands, &args, tf_decltype); + if (call == error_mark_node) + { + ++cp_unevaluated_operand; + call = build_new_function_call (cands, &args, tf_decltype); + --cp_unevaluated_operand; + } - if (t == error_mark_node && (complain & tf_warning_or_error)) + if (call == error_mark_node && (complain & tf_warning_or_error)) { error ("class template argument deduction failed:"); - t = build_new_function_call (cands, &args, complain | tf_decltype); + + ++cp_unevaluated_operand; + call = build_new_function_call (cands, &args, complain | tf_decltype); + --cp_unevaluated_operand; + if (elided) inform (input_location, "explicit deduction guides not considered " "for copy-initialization"); } - --cp_unevaluated_operand; release_tree_vector (args); - return cp_build_qualified_type (TREE_TYPE (t), cp_type_quals (ptype)); + return cp_build_qualified_type (TREE_TYPE (call), cp_type_quals (ptype)); } /* Replace occurrences of 'auto' in TYPE with the appropriate type deduced diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction41.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction41.C new file mode 100644 index 0000000..5e7fa3a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction41.C @@ -0,0 +1,19 @@ +// { dg-options -std=c++1z } + +#include <initializer_list> + +struct B { }; + +template <class T> +struct A +{ + A(std::initializer_list<T>); + A(T, B); +}; + +A a { 1, B() }; + +template <class,class> struct same; +template <class T> struct same<T,T> { }; + +same<decltype(a), A<int>> s;