简体   繁体   中英

Why are vector's multi-argument constructors taking construction parameters not marked "explicit"?

I observed the following vector constructors in the Standard C++ library

explicit vector(size_type n);
vector(size_type n, const T& value, const Allocator& = Allocator());

Is there a reason why the second constructor is not marked explicit ? This compiles, and makes me feel bad

void f(vector<string>);

int main() {
  f({10, "foo"});
}

While if I omit the "foo" , it doesn't compile and that is what I expect when I pass a pair (compound) value of an int and a string to a function that wants a vector of strings.

I'm wondering whether it is legitimate in the first place to expect that {... } always represents a list of container elements when creating a temporary. This seems to be your assumption. IMO the one-argument constructor needs to be declared as explicit to avoid undesidered conversion sequences or meaningless assignments such as:

vector<int> x = 3;

On the other hand, for the two-argument version, the only way this constructor can be called when a temporary is created is with the use of curly braces, and the programmer is well aware of what he's putting in there. For instance, it is quite clear to me that 10 and "hello" are not meant to represent a list of container elements, because 10 is not a string.

If I really wanted to pass in a vector of 10 elements initialized to "hello" , I would be bothered by having to write f(vector(10, "hello")) instead of just doing f({10, "hello"}) .

So to sum it up: while the one-argument constructor needs to be declared as explicit , I believe this is not mandatory for the two-argument value, because not everything which is inside a pair of curly braces should be interpreted as a list of container elements.

While if I omit the "foo", it doesn't compile and that is what I expect when I pass a pair (compound) value of an int and a string to a function that wants a vector of strings.

No, you don't pass a pair of int and a string but you create a vector of size 10 with content of strings like "foo". There is nothing wrong on it. I can figure some situation where it may be usefull to create a vector contains the equal strings from the beginning

that is what I expect when I pass a pair (compound) value of an int and a string to a function that wants a vector of strings.

Well, there's your problem.

{...} is not a "compound value". It is not a list. It says, "initialize an object using these values". If the object in question is an aggregate, it will use aggregate initialization. If the object in question is a non-aggregate type, it will pick a constructor to call, based on the matching constructors of the type and the various rules for braced-init-lists in C++11.

You shouldn't think of {10, "foo"} as a list of two values. It is an initializer that contains two values. It could be used with a std::pair<int, const char *> , and so forth.

The reason why std::vector 's constructor isn't explicit is precisely to allow this construct. The single-argument constructor is explicit because otherwise, implicit conversion rules would allow this:

std::vector<T> v = 5; //???

Or, more importantly:

void Foo(const std::vector<T> &v);

Foo(5); //???

We don't want integers to be implicitly convertible to std::vector s. However, when you're using an initializer, it's more reasonable to allow a wider range of "implicit" conversions, because you can see the {} syntax there.

With the single-argument case, it isn't clear what the user means. With the {} syntax, it is clear what the user means: to initialize the object.

Foo({10, "foo"}); //Initializes the first argument given the values.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM