Deduction guides, initializer_list, and the type deduction process
Consider the following code:
#include <initializer_list>
#include <utility>
template<class T>
struct test
test(const std::pair<T, T> &)
;
template<class T>
test(std::initializer_list<T>) -> test<T>;
int main()
test t1, 2;
I would like to understand why this trick with initializer_list
compiles. It looks like at first, 1, 2 is treated as an initializer_list
, but then it's re-interpreted as a list-initialization of pair
.
What exactly happens here, step-by-step?
c++ language-lawyer c++17 initializer-list list-initialization
add a comment |
Consider the following code:
#include <initializer_list>
#include <utility>
template<class T>
struct test
test(const std::pair<T, T> &)
;
template<class T>
test(std::initializer_list<T>) -> test<T>;
int main()
test t1, 2;
I would like to understand why this trick with initializer_list
compiles. It looks like at first, 1, 2 is treated as an initializer_list
, but then it's re-interpreted as a list-initialization of pair
.
What exactly happens here, step-by-step?
c++ language-lawyer c++17 initializer-list list-initialization
add a comment |
Consider the following code:
#include <initializer_list>
#include <utility>
template<class T>
struct test
test(const std::pair<T, T> &)
;
template<class T>
test(std::initializer_list<T>) -> test<T>;
int main()
test t1, 2;
I would like to understand why this trick with initializer_list
compiles. It looks like at first, 1, 2 is treated as an initializer_list
, but then it's re-interpreted as a list-initialization of pair
.
What exactly happens here, step-by-step?
c++ language-lawyer c++17 initializer-list list-initialization
Consider the following code:
#include <initializer_list>
#include <utility>
template<class T>
struct test
test(const std::pair<T, T> &)
;
template<class T>
test(std::initializer_list<T>) -> test<T>;
int main()
test t1, 2;
I would like to understand why this trick with initializer_list
compiles. It looks like at first, 1, 2 is treated as an initializer_list
, but then it's re-interpreted as a list-initialization of pair
.
What exactly happens here, step-by-step?
c++ language-lawyer c++17 initializer-list list-initialization
c++ language-lawyer c++17 initializer-list list-initialization
edited Nov 13 '18 at 14:56
Barry
180k19312572
180k19312572
asked Nov 12 '18 at 19:36
Igor R.Igor R.
10.6k12960
10.6k12960
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
It compiles because that's how class template deduction guides work.
Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.
Once the deduction is made, the actual C++ code takes over with a specific instantion of test
. So instead of test t1, 2;
, the compiler behaves as if you had said test<int> t1, 2;
.
test<int>
has a constructor that takes a pair<int, int>
, which can match the values in the braced-init-list, so that's what gets called.
This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.
So we get to have this class template deduction guide for std::array
:
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
This allows std::array arr = 2, 4, 6, 7;
to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array
gets to remain an aggregate.
I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...
– Igor R.
Nov 12 '18 at 21:23
add a comment |
With your deduction guide we end up with what is equivalent to:
test<int> t1, 2;
This works due to list initialization, section dcl.init.listp3.7 which says:
Otherwise, if T is a class type, constructors are considered. The
applicable constructors are enumerated and the best one is chosen
through overload resolution ([over.match], [over.match.list]). If a
narrowing conversion (see below) is required to convert any of the
arguments, the program is ill-formed. [ Example:struct S
S(std::initializer_list<double>); // #1
S(std::initializer_list<int>); // #2
S(); // #3
// ...
;
S s1 = 1.0, 2.0, 3.0 ; // invoke #1
S s2 = 1, 2, 3 ; // invoke #2
S s3 = ; // invoke #3
— end example ] [ Example:
struct Map
Map(std::initializer_list<std::pair<std::string,int>>);
;
Map ship = "Sophie",14, "Surprise",28;
— end example ] [ Example:
struct S
// no initializer-list constructors
S(int, double, double); // #1
S(); // #2
// ...
;
S s1 = 1, 2, 3.0 ; // OK: invoke #1
S s2 1.0, 2, 3 ; // error: narrowing
S s3 ; // OK: invoke #2
— end example ]
Otherwise we have a non-deduced context
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53268957%2fdeduction-guides-initializer-list-and-the-type-deduction-process%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
It compiles because that's how class template deduction guides work.
Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.
Once the deduction is made, the actual C++ code takes over with a specific instantion of test
. So instead of test t1, 2;
, the compiler behaves as if you had said test<int> t1, 2;
.
test<int>
has a constructor that takes a pair<int, int>
, which can match the values in the braced-init-list, so that's what gets called.
This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.
So we get to have this class template deduction guide for std::array
:
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
This allows std::array arr = 2, 4, 6, 7;
to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array
gets to remain an aggregate.
I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...
– Igor R.
Nov 12 '18 at 21:23
add a comment |
It compiles because that's how class template deduction guides work.
Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.
Once the deduction is made, the actual C++ code takes over with a specific instantion of test
. So instead of test t1, 2;
, the compiler behaves as if you had said test<int> t1, 2;
.
test<int>
has a constructor that takes a pair<int, int>
, which can match the values in the braced-init-list, so that's what gets called.
This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.
So we get to have this class template deduction guide for std::array
:
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
This allows std::array arr = 2, 4, 6, 7;
to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array
gets to remain an aggregate.
I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...
– Igor R.
Nov 12 '18 at 21:23
add a comment |
It compiles because that's how class template deduction guides work.
Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.
Once the deduction is made, the actual C++ code takes over with a specific instantion of test
. So instead of test t1, 2;
, the compiler behaves as if you had said test<int> t1, 2;
.
test<int>
has a constructor that takes a pair<int, int>
, which can match the values in the braced-init-list, so that's what gets called.
This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.
So we get to have this class template deduction guide for std::array
:
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
This allows std::array arr = 2, 4, 6, 7;
to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array
gets to remain an aggregate.
It compiles because that's how class template deduction guides work.
Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.
Once the deduction is made, the actual C++ code takes over with a specific instantion of test
. So instead of test t1, 2;
, the compiler behaves as if you had said test<int> t1, 2;
.
test<int>
has a constructor that takes a pair<int, int>
, which can match the values in the braced-init-list, so that's what gets called.
This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.
So we get to have this class template deduction guide for std::array
:
template <class T, class... U>
array(T, U...) -> array<T, 1 + sizeof...(U)>;
This allows std::array arr = 2, 4, 6, 7;
to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array
gets to remain an aggregate.
edited Nov 12 '18 at 20:38
answered Nov 12 '18 at 20:32
Nicol BolasNicol Bolas
285k33471645
285k33471645
I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...
– Igor R.
Nov 12 '18 at 21:23
add a comment |
I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...
– Igor R.
Nov 12 '18 at 21:23
I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...
– Igor R.
Nov 12 '18 at 21:23
I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...
– Igor R.
Nov 12 '18 at 21:23
add a comment |
With your deduction guide we end up with what is equivalent to:
test<int> t1, 2;
This works due to list initialization, section dcl.init.listp3.7 which says:
Otherwise, if T is a class type, constructors are considered. The
applicable constructors are enumerated and the best one is chosen
through overload resolution ([over.match], [over.match.list]). If a
narrowing conversion (see below) is required to convert any of the
arguments, the program is ill-formed. [ Example:struct S
S(std::initializer_list<double>); // #1
S(std::initializer_list<int>); // #2
S(); // #3
// ...
;
S s1 = 1.0, 2.0, 3.0 ; // invoke #1
S s2 = 1, 2, 3 ; // invoke #2
S s3 = ; // invoke #3
— end example ] [ Example:
struct Map
Map(std::initializer_list<std::pair<std::string,int>>);
;
Map ship = "Sophie",14, "Surprise",28;
— end example ] [ Example:
struct S
// no initializer-list constructors
S(int, double, double); // #1
S(); // #2
// ...
;
S s1 = 1, 2, 3.0 ; // OK: invoke #1
S s2 1.0, 2, 3 ; // error: narrowing
S s3 ; // OK: invoke #2
— end example ]
Otherwise we have a non-deduced context
add a comment |
With your deduction guide we end up with what is equivalent to:
test<int> t1, 2;
This works due to list initialization, section dcl.init.listp3.7 which says:
Otherwise, if T is a class type, constructors are considered. The
applicable constructors are enumerated and the best one is chosen
through overload resolution ([over.match], [over.match.list]). If a
narrowing conversion (see below) is required to convert any of the
arguments, the program is ill-formed. [ Example:struct S
S(std::initializer_list<double>); // #1
S(std::initializer_list<int>); // #2
S(); // #3
// ...
;
S s1 = 1.0, 2.0, 3.0 ; // invoke #1
S s2 = 1, 2, 3 ; // invoke #2
S s3 = ; // invoke #3
— end example ] [ Example:
struct Map
Map(std::initializer_list<std::pair<std::string,int>>);
;
Map ship = "Sophie",14, "Surprise",28;
— end example ] [ Example:
struct S
// no initializer-list constructors
S(int, double, double); // #1
S(); // #2
// ...
;
S s1 = 1, 2, 3.0 ; // OK: invoke #1
S s2 1.0, 2, 3 ; // error: narrowing
S s3 ; // OK: invoke #2
— end example ]
Otherwise we have a non-deduced context
add a comment |
With your deduction guide we end up with what is equivalent to:
test<int> t1, 2;
This works due to list initialization, section dcl.init.listp3.7 which says:
Otherwise, if T is a class type, constructors are considered. The
applicable constructors are enumerated and the best one is chosen
through overload resolution ([over.match], [over.match.list]). If a
narrowing conversion (see below) is required to convert any of the
arguments, the program is ill-formed. [ Example:struct S
S(std::initializer_list<double>); // #1
S(std::initializer_list<int>); // #2
S(); // #3
// ...
;
S s1 = 1.0, 2.0, 3.0 ; // invoke #1
S s2 = 1, 2, 3 ; // invoke #2
S s3 = ; // invoke #3
— end example ] [ Example:
struct Map
Map(std::initializer_list<std::pair<std::string,int>>);
;
Map ship = "Sophie",14, "Surprise",28;
— end example ] [ Example:
struct S
// no initializer-list constructors
S(int, double, double); // #1
S(); // #2
// ...
;
S s1 = 1, 2, 3.0 ; // OK: invoke #1
S s2 1.0, 2, 3 ; // error: narrowing
S s3 ; // OK: invoke #2
— end example ]
Otherwise we have a non-deduced context
With your deduction guide we end up with what is equivalent to:
test<int> t1, 2;
This works due to list initialization, section dcl.init.listp3.7 which says:
Otherwise, if T is a class type, constructors are considered. The
applicable constructors are enumerated and the best one is chosen
through overload resolution ([over.match], [over.match.list]). If a
narrowing conversion (see below) is required to convert any of the
arguments, the program is ill-formed. [ Example:struct S
S(std::initializer_list<double>); // #1
S(std::initializer_list<int>); // #2
S(); // #3
// ...
;
S s1 = 1.0, 2.0, 3.0 ; // invoke #1
S s2 = 1, 2, 3 ; // invoke #2
S s3 = ; // invoke #3
— end example ] [ Example:
struct Map
Map(std::initializer_list<std::pair<std::string,int>>);
;
Map ship = "Sophie",14, "Surprise",28;
— end example ] [ Example:
struct S
// no initializer-list constructors
S(int, double, double); // #1
S(); // #2
// ...
;
S s1 = 1, 2, 3.0 ; // OK: invoke #1
S s2 1.0, 2, 3 ; // error: narrowing
S s3 ; // OK: invoke #2
— end example ]
Otherwise we have a non-deduced context
answered Nov 12 '18 at 20:34
Shafik YaghmourShafik Yaghmour
126k23324535
126k23324535
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53268957%2fdeduction-guides-initializer-list-and-the-type-deduction-process%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown