Why does std::initializer_list in ctor not behave as expected?
Asked Answered
A

2

18
#include <vector>

int main()
{
    auto v = std::vector{std::vector<int>{}};
    return v.front().empty(); // error
}

See online demo

However, according to Scott Meyers' Effective Modern C++ (emphasis in original):

If, however, one or more constructors declare a parameter of type std::initializer_list, calls using the braced initialization syntax strongly prefer the overloads taking std::initializer_lists. Strongly. If there's any way for compilers to construe a call using a braced initializer to be a constructor taking a std::initializer_list, compilers will employ that interpretation.

So, I think std::vector{std::vector<int>{}}; should produce an object of std::vector<std::vector<int>> rather than std::vector<int>.

Who is wrong? and why?

Aida answered 30/8, 2021 at 7:18 Comment(10)
CTAD for std::vector{std::vector<int>{}} founds copy/move constructor.Slump
Resolves to calling the copy constructor.Dispute
cppinsights.io/s/05a45bbb fix: auto v = std::vector{{std::vector<int>{}}}; cppinsights.io/s/7a34c18eBriannabrianne
Why does the ctor with std::initializer_list not take the priority as Scott said? @SlumpAida
Did you mean : auto v = std::vector<std::vector<int>>{ std::vector<int>{} }; that will create the vector you want.Dispute
Why does CTAD not work in such a case?@PKramerAida
What compiler are you using? Which version of it?Ticker
CTAD does work exactly how it should, just not as you expected. It finds the best (simplest) match for your codeDispute
Does he talk about CTAD or regular constructors calls?Slump
@Slump I think he does in his example the type of std::vector is left open to be deduced from the arguments.Dispute
B
8

Meyers is mostly correct (the exception is that T{} is value-initialization if a default constructor exists), but his statement is about overload resolution. That takes place after CTAD, which chooses the class (and hence the set of constructors) to use.

CTAD doesn’t “prefer” initializer-list constructors in that it prefers copying to wrapping for nestable templates like std::vector or std::optional. (It’s possible to override this with deduction guides, but the standard library uses the default, as one might expect.) This makes some sense in that it prevents creating strange types like std::optional<std::optional<int>>, but it makes generic code harder to write because it gives

template<class T> void f(T x) {
  std::vector v{x};
  // …
}

a meaning that depends on the type of its argument in an irregular and non-injective fashion. In particular, v might be std::vector<int> with T=int or with T=std::vector<int>, despite being std::vector<std::deque<int>> if T=std::deque<int>. It’s unfortunate that a tool for computing one type based on some others is not usable in a generic context.

Brittenybrittingham answered 30/8, 2021 at 14:11 Comment(0)
H
6

auto v = std::vector{std::vector<int>{}}; actually creates a std::vector<int> because it uses std::vector copy constructor. It is interpreted by the compiler as:

auto vTemp = std::vector<int>{};
auto v = std::vector<int>( vTemp );

So v ends up being a std::vector<int>, not a std::vector<std::vector<int>>.

As reported by "P Kramer" and "Marek P" in comments, the following syntaxes will help any compiler accomplishing what you expect:

auto v = std::vector{{std::vector<int>{}}};
auto v = std::vector<std::vector<int>>{ std::vector<int>{} };
Hines answered 30/8, 2021 at 7:24 Comment(3)
Why does the ctor with std::initializer_list not take the priority as Scott said?Aida
That may not be the first time a compiler does not respect what Scott says. Note that MSVC compiler even refuses to compile the first line, it reports use of class template requires template argument list...and with argument list there would be no more ambiguity.Hines
Except for the suggestions, this merely restates the situation in the question. That “interpretation” (which gets the value category and lifetime of the argument wrong) doesn’t illustrate why std::vector<int> is chosen.Brittenybrittingham

© 2022 - 2024 — McMap. All rights reserved.