Is Clang or GCC correct in rejecting/accepting this CTAD code?

1k Views Asked by At

Clang and GCC disagree on accepting this code.

What is the standard required behavior?

#include <utility>
#include <iostream>
#include <vector>

int main()
{
    std::vector pairs = {std::pair{1,11},{2,22}, {3,33}};
    for (const auto& p: pairs) {
        std::cout << p.second << std::endl;
    }
}

Note: I know this is C++ so it is possible that the standard is fuzzy, but I presume one behavior is correct.

1

There are 1 best solutions below

2
On BEST ANSWER

The process of CTAD is largely defined by [over.match.class.deduct]. Broadly speaking, the overload set is all of the accessible constructors of the type, plus any deduction guides. And the overload set is resolved per this rule:

Initialization and overload resolution are performed as described in [dcl.init] and [over.match.ctor], [over.match.copy], or [over.match.list] (as appropriate for the type of initialization performed)

Since the "type of initialization performed" is definitely list-initialization, we move on to [over.match.list]. Where we get this infamous bulleted list:

  • Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument.

  • If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

This tells us that initializer-list constructors are prioritized; they're given first crack at overload resolution, and in a very specific way (that is, building a std::initializer_list and passing it as an argument). However, Clang gives a telling error:

note: candidate function template not viable: requires at most 2 arguments, but 3 were provided
     vector(initializer_list<value_type> __l,

That is, it's trying to call that constructor as if "the argument list consists of the elements of the initializer list." This suggests that Clang skipped the first bullet point when doing list-initialization through CTAD. The fact that adding parens around the braced-init-list "fixes" the issue also suggests that this is what is happening.

Oddly, it works for simple types, like an initializer-list of integers. And it works if you explicitly name each member as a pair. And Clang can auto-deduce the type of the initializer_list produced by {std::pair{1,11}, {2,22}, {3,33}} just fine. So this bug seems really specific.