admin管理员组文章数量:1328037
Question- see Compiler Explorer
If I create an std::vector<int>
and initialize it two ways, both call the std::initializer_list
constructor.
std::vector<int> v1{1, 2, 3}; // Calls vector(initializer_list< int >)
std::vector<int> v2 = {1, 2, 3}; // Calls vector(initializer_list< int >)
But if I use a class that implicitly converts from int
:
struct Imp { Imp(int) {} };
Then the second method of initialization calls the constructor that takes a pair of iterators.
std::vector<Imp> v3{1, 2, 3}; // Calls vector(initializer_list< Imp >)
std::vector<Imp> v4 = {1, 2, 3}; // Calls vector(const int*, const int* ) ???
This only happens using GCC 14.2. When using Clang 19.1 or MSVC 19.40, both v3
and v4
call the initializer_list
constructor. Optimization level doesn't make a difference
Why does the = {}
syntax call vector(const int*, const int*)
on GCC?
Further investigation
I tried creating my own class template that has both constructors:
template <typename T>
struct Custom {
Custom(std::initializer_list<T>) {}
Custom(const int*, const int*) {}
};
Now both the {}
and = {}
syntax call the initializer_list
constructor on GCC.
Custom<Imp> v5{1, 2, 3}; // Calls Custom(initializer_list< Imp >)
Custom<Imp> v6 = {1, 2, 3}; // Calls Custom(initializer_list< Imp >)
Where things get really confusing, is if I specialize std::vector
for my implicitly-converting type Imp
. Then there are three cases:
Case 1 - Provide initializer_list
ctor only
template <>
struct std::vector<Imp> {
vector(initializer_list<Imp>) {}
};
Result - Both syntaxes call the provided ctor
std::vector<Imp> v7{1, 2, 3}; // Calls vector(initializer_list< Imp >)
std::vector<Imp> v8 = {1, 2, 3}; // Calls vector(initializer_list< Imp >)
Case 2 - Provide const int*
ctor only
template <>
struct std::vector<Imp> {
vector(const int*, const int*) {}
};
Result - Both syntaxes fail to compile (no matching ctor)
std::vector<Imp> v9{1, 2, 3}; // Error - no matching ctor
std::vector<Imp> vA = {1, 2, 3}; // Error - no matching ctor
Case 3 - Provide both ctors
template <>
struct std::vector<Imp> {
vector(initializer_list<Imp>) {}
vector(const int*, const int*) {}
};
Result - The {}
syntax calls the initializer_list
ctor, while the = {}
syntax calls the const int*
ctor
std::vector<Imp> vB{1, 2, 3}; // Calls vector(initializer_list< Imp >)
std::vector<Imp> vC = {1, 2, 3}; // Calls vector(const int*, const int*) ???
Here is where I am lost.
In case 3, how does providing the initializer_list
constructor allow the const int*
constructor to be called?
EDIT
It was asked how I know which constructor is being called. This started when I was looking at when copy vs. move constructors are called. std::initializer_list
cannot be moved from. But here are some additional examples, with prints.
Example 1 - Using a standalone type
struct Imp { Imp(int) {} };
template <typename T>
struct Custom {
Custom(std::initializer_list<T>) { std::printf("initializer_list<T>\n"); }
Custom(const int*, const int*) { std::printf("const int*, const int*\n"); }
};
int main(int argc, char *argv[]) {
Custom<Imp> v{1, 2, 3};
Custom<Imp> w = {1, 2, 3};
Prints:
initializer_list<T>
initializer_list<T>
Example 2 - Using an std::vector specialization
struct Imp { Imp(int) {} };
template <>
struct std::vector<Imp> {
vector(initializer_list<Imp>) { std::printf("initializer_list<T>\n"); }
vector(const int*, const int*) { std::printf("const int*, const int*\n"); }
};
int main(int argc, char *argv[]) {
std::vector<Imp> v{1, 2, 3};
std::vector<Imp> w = {1, 2, 3};
Prints:
initializer_list<T>
const int*, const int*
The only difference between the two examples is that one is an std:: specialization, and the other is not.
Why would this change which ctor overload is called?
EDIT2
After the answer by cpplearner I ran a test which initializes an std::vector
with string
literals many times, using the v{}
and v = {}
syntaxes.
On my i9-10885H:
GCC 14.2
v{} 0.820 seconds
v={} 0.444 seconds
v={} took 54.1% as long as v{}
Clang 18.1
v{} 0.819 seconds
v={} 0.842 seconds
v={} took 102.8% as long as v{}
So it does appear this is a string
literal optimization in GCC, which only works for the v = {}
syntax.
Question- see Compiler Explorer
If I create an std::vector<int>
and initialize it two ways, both call the std::initializer_list
constructor.
std::vector<int> v1{1, 2, 3}; // Calls vector(initializer_list< int >)
std::vector<int> v2 = {1, 2, 3}; // Calls vector(initializer_list< int >)
But if I use a class that implicitly converts from int
:
struct Imp { Imp(int) {} };
Then the second method of initialization calls the constructor that takes a pair of iterators.
std::vector<Imp> v3{1, 2, 3}; // Calls vector(initializer_list< Imp >)
std::vector<Imp> v4 = {1, 2, 3}; // Calls vector(const int*, const int* ) ???
This only happens using GCC 14.2. When using Clang 19.1 or MSVC 19.40, both v3
and v4
call the initializer_list
constructor. Optimization level doesn't make a difference
Why does the = {}
syntax call vector(const int*, const int*)
on GCC?
Further investigation
I tried creating my own class template that has both constructors:
template <typename T>
struct Custom {
Custom(std::initializer_list<T>) {}
Custom(const int*, const int*) {}
};
Now both the {}
and = {}
syntax call the initializer_list
constructor on GCC.
Custom<Imp> v5{1, 2, 3}; // Calls Custom(initializer_list< Imp >)
Custom<Imp> v6 = {1, 2, 3}; // Calls Custom(initializer_list< Imp >)
Where things get really confusing, is if I specialize std::vector
for my implicitly-converting type Imp
. Then there are three cases:
Case 1 - Provide initializer_list
ctor only
template <>
struct std::vector<Imp> {
vector(initializer_list<Imp>) {}
};
Result - Both syntaxes call the provided ctor
std::vector<Imp> v7{1, 2, 3}; // Calls vector(initializer_list< Imp >)
std::vector<Imp> v8 = {1, 2, 3}; // Calls vector(initializer_list< Imp >)
Case 2 - Provide const int*
ctor only
template <>
struct std::vector<Imp> {
vector(const int*, const int*) {}
};
Result - Both syntaxes fail to compile (no matching ctor)
std::vector<Imp> v9{1, 2, 3}; // Error - no matching ctor
std::vector<Imp> vA = {1, 2, 3}; // Error - no matching ctor
Case 3 - Provide both ctors
template <>
struct std::vector<Imp> {
vector(initializer_list<Imp>) {}
vector(const int*, const int*) {}
};
Result - The {}
syntax calls the initializer_list
ctor, while the = {}
syntax calls the const int*
ctor
std::vector<Imp> vB{1, 2, 3}; // Calls vector(initializer_list< Imp >)
std::vector<Imp> vC = {1, 2, 3}; // Calls vector(const int*, const int*) ???
Here is where I am lost.
In case 3, how does providing the initializer_list
constructor allow the const int*
constructor to be called?
EDIT
It was asked how I know which constructor is being called. This started when I was looking at when copy vs. move constructors are called. std::initializer_list
cannot be moved from. But here are some additional examples, with prints.
Example 1 - Using a standalone type
struct Imp { Imp(int) {} };
template <typename T>
struct Custom {
Custom(std::initializer_list<T>) { std::printf("initializer_list<T>\n"); }
Custom(const int*, const int*) { std::printf("const int*, const int*\n"); }
};
int main(int argc, char *argv[]) {
Custom<Imp> v{1, 2, 3};
Custom<Imp> w = {1, 2, 3};
Prints:
initializer_list<T>
initializer_list<T>
Example 2 - Using an std::vector specialization
struct Imp { Imp(int) {} };
template <>
struct std::vector<Imp> {
vector(initializer_list<Imp>) { std::printf("initializer_list<T>\n"); }
vector(const int*, const int*) { std::printf("const int*, const int*\n"); }
};
int main(int argc, char *argv[]) {
std::vector<Imp> v{1, 2, 3};
std::vector<Imp> w = {1, 2, 3};
Prints:
initializer_list<T>
const int*, const int*
The only difference between the two examples is that one is an std:: specialization, and the other is not.
Why would this change which ctor overload is called?
EDIT2
After the answer by cpplearner I ran a test which initializes an std::vector
with string
literals many times, using the v{}
and v = {}
syntaxes.
On my i9-10885H:
GCC 14.2
v{} 0.820 seconds
v={} 0.444 seconds
v={} took 54.1% as long as v{}
Clang 18.1
v{} 0.819 seconds
v={} 0.842 seconds
v={} took 102.8% as long as v{}
So it does appear this is a string
literal optimization in GCC, which only works for the v = {}
syntax.
1 Answer
Reset to default 15Congratulations, you just found a secret optimization done by GCC!
Since GCC 13, GCC rewrites certain list-initializations (which should call the initializer_list
constructor) to use the iterator-pair constructor instead. This is intended to work around the inefficiency when constructing a vector<string>
with a list of string literals. See GCC bug 105838.
This behavior is described in GCC's source code.
If we were going to call e.g. vector(initializer_list<string>) starting with a list of string-literals (which is inefficient, see PR105838), instead build an array of const char* and pass it to the range constructor. But only do this for standard library types, where we can assume the transformation makes sense.
As to why this optimization is applied to std::vector<T> v = {1, 2, 3}
but not to std::vector<T> v{1, 2, 3}
, it's probably because direct-initialization is represented differently in GCC, which causes it to not be detected by this optimization.
本文标签:
版权声明:本文标题:c++ - Why do std::vector<T> v{1, 2, 3} and std::vector<T> v = {1, 2, 3} call different constructors, 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738745118a2110074.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
const int *, const int *
one internally. – HolyBlackCat Commented Jan 19 at 7:19