std::variant converting constructor doesn't handle const volatile qualifiersGCC warning about implicit dereferenceImplementing std::variant converting constructor - or: how to find first overload of all conversions from any T to Ti from parameter packHow to convert std::string to lower case?How to convert a std::string to const char* or char*?Are the days of passing const std::string & as a parameter over?Implementing std::variant converting constructor - or: how to find first overload of all conversions from any T to Ti from parameter packRecursively visiting an `std::variant` using lambdas and fixed-point combinatorsWhy can I not std::partition this std::unordered_map?C++ variant converting constructor with boolSFINAE on array initialization in VS10
2000s space film where an alien species has almost wiped out the human race in a war
What is a "major country" as named in Bernie Sanders' Healthcare debate answers?
Telling my mother that I have anorexia without panicking her
Should you only use colons and periods in dialogues?
How are unbalanced coaxial cables used for broadcasting TV signals without any problems?
If I want an interpretable model, are there methods other than Linear Regression?
Would it be unbalanced to increase Wild Shape uses based on level?
What exactly is a marshrutka (маршрутка)?
Can druids change their starting cantrips each day?
I am getting "syntax error near unexpected token `'$#''" in a simple Bash script
Changing trains at Firenze S.M.N. train station - how fast can it be done?
How to control the output voltage of a solid state relay
How would you control supersoldiers in a late iron-age society?
Some Prime Peerage
Which is the current decimal separator?
The Planck constant for mathematicians
Are there any “Third Order” acronyms used in space exploration?
Has SHA256 been broken by Treadwell Stanton DuPont?
Why are some files not movable on Windows 10?
Is there any way to land a rover on the Moon without using any thrusters?
How do we know that black holes are spinning?
Why does the speed of sound decrease at high altitudes although the air density decreases?
Should I put criteria inside my join or where clause when optimizing?
Real mode flat model
std::variant converting constructor doesn't handle const volatile qualifiers
GCC warning about implicit dereferenceImplementing std::variant converting constructor - or: how to find first overload of all conversions from any T to Ti from parameter packHow to convert std::string to lower case?How to convert a std::string to const char* or char*?Are the days of passing const std::string & as a parameter over?Implementing std::variant converting constructor - or: how to find first overload of all conversions from any T to Ti from parameter packRecursively visiting an `std::variant` using lambdas and fixed-point combinatorsWhy can I not std::partition this std::unordered_map?C++ variant converting constructor with boolSFINAE on array initialization in VS10
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
The code below:
int i = 1;
const int i_c = 2;
volatile int i_v = 3;
const volatile int i_cv = 4;
typedef std::variant<int, const int, volatile int, const volatile int> TVariant;
TVariant var (i );
TVariant var_c (i_c );
TVariant var_v (i_v );
TVariant var_cv(i_cv);
std::cerr << std::boolalpha;
std::cerr << std::holds_alternative< int>(var ) << std::endl;
std::cerr << std::holds_alternative<const int>(var_c ) << std::endl;
std::cerr << std::holds_alternative< volatile int>(var_v ) << std::endl;
std::cerr << std::holds_alternative<const volatile int>(var_cv) << std::endl;
std::cerr << var .index() << std::endl;
std::cerr << var_c .index() << std::endl;
std::cerr << var_v .index() << std::endl;
std::cerr << var_cv.index() << std::endl;
outputs:
true
false
false
false
0
0
0
0
coliru
And so std::variant converting constructor doesn't take into account const volatile qualifier of the converting-from type. Is it expected behavior?
Information about converting constructor from cppreference.com
Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression
F(std::forward<T>(t))if there was an overload of imaginary function F(T_i) for every T_i from Types...
The problem is that in the case above the overload set of such imaginary function is ambiguous:
void F( int)
void F(const int)
void F( volatile int)
void F(const volatile int)
coliru
cppreference.com says nothing about this case. Does the standard specify this?
I'm making my own implementation of std::variant class. My implementation of converting constructor is based on this idea. And the result is the same as shown above (the first suitable alternative is selected, even though there are others). libstdc++ probably implements it in the same way, because it also selects the first suitable alternative. But I'm still wondering if this is correct behavior.
c++ variant c++-standard-library
add a comment
|
The code below:
int i = 1;
const int i_c = 2;
volatile int i_v = 3;
const volatile int i_cv = 4;
typedef std::variant<int, const int, volatile int, const volatile int> TVariant;
TVariant var (i );
TVariant var_c (i_c );
TVariant var_v (i_v );
TVariant var_cv(i_cv);
std::cerr << std::boolalpha;
std::cerr << std::holds_alternative< int>(var ) << std::endl;
std::cerr << std::holds_alternative<const int>(var_c ) << std::endl;
std::cerr << std::holds_alternative< volatile int>(var_v ) << std::endl;
std::cerr << std::holds_alternative<const volatile int>(var_cv) << std::endl;
std::cerr << var .index() << std::endl;
std::cerr << var_c .index() << std::endl;
std::cerr << var_v .index() << std::endl;
std::cerr << var_cv.index() << std::endl;
outputs:
true
false
false
false
0
0
0
0
coliru
And so std::variant converting constructor doesn't take into account const volatile qualifier of the converting-from type. Is it expected behavior?
Information about converting constructor from cppreference.com
Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression
F(std::forward<T>(t))if there was an overload of imaginary function F(T_i) for every T_i from Types...
The problem is that in the case above the overload set of such imaginary function is ambiguous:
void F( int)
void F(const int)
void F( volatile int)
void F(const volatile int)
coliru
cppreference.com says nothing about this case. Does the standard specify this?
I'm making my own implementation of std::variant class. My implementation of converting constructor is based on this idea. And the result is the same as shown above (the first suitable alternative is selected, even though there are others). libstdc++ probably implements it in the same way, because it also selects the first suitable alternative. But I'm still wondering if this is correct behavior.
c++ variant c++-standard-library
Note thatauto a = var_cwill discardconst, andauto b = var_vwill discardvolatileand this is source of your problems.intis always auto deduced when calling constructor.
– Marek R
8 hours ago
@MarekR Auto deduced where? Variant's constructor uses a forwarding reference, whereconstandvolatilequalifiers are preserved in a template parameter, IIRC (live demo). I believe the problem is with those imaginaryFUNfunctions that pass by value.
– Daniel Langr
8 hours ago
add a comment
|
The code below:
int i = 1;
const int i_c = 2;
volatile int i_v = 3;
const volatile int i_cv = 4;
typedef std::variant<int, const int, volatile int, const volatile int> TVariant;
TVariant var (i );
TVariant var_c (i_c );
TVariant var_v (i_v );
TVariant var_cv(i_cv);
std::cerr << std::boolalpha;
std::cerr << std::holds_alternative< int>(var ) << std::endl;
std::cerr << std::holds_alternative<const int>(var_c ) << std::endl;
std::cerr << std::holds_alternative< volatile int>(var_v ) << std::endl;
std::cerr << std::holds_alternative<const volatile int>(var_cv) << std::endl;
std::cerr << var .index() << std::endl;
std::cerr << var_c .index() << std::endl;
std::cerr << var_v .index() << std::endl;
std::cerr << var_cv.index() << std::endl;
outputs:
true
false
false
false
0
0
0
0
coliru
And so std::variant converting constructor doesn't take into account const volatile qualifier of the converting-from type. Is it expected behavior?
Information about converting constructor from cppreference.com
Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression
F(std::forward<T>(t))if there was an overload of imaginary function F(T_i) for every T_i from Types...
The problem is that in the case above the overload set of such imaginary function is ambiguous:
void F( int)
void F(const int)
void F( volatile int)
void F(const volatile int)
coliru
cppreference.com says nothing about this case. Does the standard specify this?
I'm making my own implementation of std::variant class. My implementation of converting constructor is based on this idea. And the result is the same as shown above (the first suitable alternative is selected, even though there are others). libstdc++ probably implements it in the same way, because it also selects the first suitable alternative. But I'm still wondering if this is correct behavior.
c++ variant c++-standard-library
The code below:
int i = 1;
const int i_c = 2;
volatile int i_v = 3;
const volatile int i_cv = 4;
typedef std::variant<int, const int, volatile int, const volatile int> TVariant;
TVariant var (i );
TVariant var_c (i_c );
TVariant var_v (i_v );
TVariant var_cv(i_cv);
std::cerr << std::boolalpha;
std::cerr << std::holds_alternative< int>(var ) << std::endl;
std::cerr << std::holds_alternative<const int>(var_c ) << std::endl;
std::cerr << std::holds_alternative< volatile int>(var_v ) << std::endl;
std::cerr << std::holds_alternative<const volatile int>(var_cv) << std::endl;
std::cerr << var .index() << std::endl;
std::cerr << var_c .index() << std::endl;
std::cerr << var_v .index() << std::endl;
std::cerr << var_cv.index() << std::endl;
outputs:
true
false
false
false
0
0
0
0
coliru
And so std::variant converting constructor doesn't take into account const volatile qualifier of the converting-from type. Is it expected behavior?
Information about converting constructor from cppreference.com
Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression
F(std::forward<T>(t))if there was an overload of imaginary function F(T_i) for every T_i from Types...
The problem is that in the case above the overload set of such imaginary function is ambiguous:
void F( int)
void F(const int)
void F( volatile int)
void F(const volatile int)
coliru
cppreference.com says nothing about this case. Does the standard specify this?
I'm making my own implementation of std::variant class. My implementation of converting constructor is based on this idea. And the result is the same as shown above (the first suitable alternative is selected, even though there are others). libstdc++ probably implements it in the same way, because it also selects the first suitable alternative. But I'm still wondering if this is correct behavior.
c++ variant c++-standard-library
c++ variant c++-standard-library
edited 7 hours ago
anton_rh
asked 8 hours ago
anton_rhanton_rh
2,44619 silver badges38 bronze badges
2,44619 silver badges38 bronze badges
Note thatauto a = var_cwill discardconst, andauto b = var_vwill discardvolatileand this is source of your problems.intis always auto deduced when calling constructor.
– Marek R
8 hours ago
@MarekR Auto deduced where? Variant's constructor uses a forwarding reference, whereconstandvolatilequalifiers are preserved in a template parameter, IIRC (live demo). I believe the problem is with those imaginaryFUNfunctions that pass by value.
– Daniel Langr
8 hours ago
add a comment
|
Note thatauto a = var_cwill discardconst, andauto b = var_vwill discardvolatileand this is source of your problems.intis always auto deduced when calling constructor.
– Marek R
8 hours ago
@MarekR Auto deduced where? Variant's constructor uses a forwarding reference, whereconstandvolatilequalifiers are preserved in a template parameter, IIRC (live demo). I believe the problem is with those imaginaryFUNfunctions that pass by value.
– Daniel Langr
8 hours ago
Note that
auto a = var_c will discard const, and auto b = var_v will discard volatile and this is source of your problems. int is always auto deduced when calling constructor.– Marek R
8 hours ago
Note that
auto a = var_c will discard const, and auto b = var_v will discard volatile and this is source of your problems. int is always auto deduced when calling constructor.– Marek R
8 hours ago
@MarekR Auto deduced where? Variant's constructor uses a forwarding reference, where
const and volatile qualifiers are preserved in a template parameter, IIRC (live demo). I believe the problem is with those imaginary FUN functions that pass by value.– Daniel Langr
8 hours ago
@MarekR Auto deduced where? Variant's constructor uses a forwarding reference, where
const and volatile qualifiers are preserved in a template parameter, IIRC (live demo). I believe the problem is with those imaginary FUN functions that pass by value.– Daniel Langr
8 hours ago
add a comment
|
3 Answers
3
active
oldest
votes
Yeah, this is just how functions work when you pass by value.
The function void foo(int) and the function void foo(const int) and the function void foo(volatile int) and the function void foo(const volatile int) are all the same function.
By extension, there is no distinction for your variant's converting constructor to make, and no meaningful way to use a variant whose alternatives differ only in their top-level cv-qualifier.
(Well, okay, you can emplace with an explicit template argument, as Marek shows, but why? To what end?)
[dcl.fct/5][..] After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. [..]
3
I'm just wondering why the code compiles. Shouldn't it, according to eel.is/c++draft/variant.ctor#12.sentence-2, produce some ambiguity error in case there are multiple viable overloads? Is the code well-formed or not? (Kind-of a relevant case is mentioned in this note: eel.is/c++draft/variant.ctor#17.note-1, though there are same types).
– Daniel Langr
8 hours ago
@DanielLangr IMO, there are no multiple viable overloads. There is only one converting constructor (after adjustment), possibly multiply-defined (though that's okay for templates). Not really sure whether this is all well-formed though.
– Lightness Races in Orbit
7 hours ago
@DanielLangr That note is interesting. Okay maybe it's not well-formed then :P
– Lightness Races in Orbit
7 hours ago
Not multiple overloads of the constructor. I meant multiple overloads of those imaginaryfunfunctions I linked.
– Daniel Langr
7 hours ago
add a comment
|
Note that you are creating copy of value. This means that const and volatile modifiers can be safely discarded. That is why template always deduces int.
You can force specific type using emplace.
See demo https://coliru.stacked-crooked.com/a/4dd054dc4fa9bb9a
2
Those warnings are a bit alarming - what do they mean?
– Lightness Races in Orbit
8 hours ago
@LightnessRacesinOrbit See here.emplacereturns the constructed value, andgccwarns that discarding that reference tovolatilereturn value does not perform a read on it. The warning goes away if you actually do read from the returned reference tovolatile: godbolt.org/z/F24Iea
– Max Langhof
8 hours ago
@MaxLanghof Got ya.
– Lightness Races in Orbit
7 hours ago
add a comment
|
My reading of the standard is that the code should be ill-formed due to ambiguity. It surprises me that both libstdc++ and libc++ appear to allow it.
Here's what [variant.ctor]/12 says:
Let
T_j be a type that is determined as follows: build an imaginary function FUN(T_i) for each alternative typeT_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t))defines the alternativeT_j which is the type of the contained value after construction.
So four functions are created: initially FUN(int), FUN(const int), FUN(volatile int), and FUN(const volatile int). These are all equivalent signatures, so they could not be overloaded with each other. This paragraph does not really specify what should happen if the overload set cannot actually be built. However, there is a note that strongly implies a particular interpretation:
[ Note:
variant<string, string> v("abc");
is ill-formed, as both alternative types have an equally viable constructor for the argument. —end note]
This note is basically saying that overload resolution cannot distinguish between string and string. In order for that to happen, overload resolution must be done even though the signatures are the same. The two FUN(string)s are not collapsed into a single function.
Note that overload resolution is allowed to consider overloads with identical signatures due to templates. For example:
template <class T> struct Id1 using type = T; ;
template <class T> struct Id2 using type = T; ;
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous
Here, there are two identical signatures of f, and both are submitted to overload resolution but neither is better than the other.
Going back to the Standard's example, it seems that the prescription is to apply the overload resolution procedure even if some of the overloads could not be overloaded with each other as ordinary function declarations. (If you want, imagine that they are all instantiated from templates.) Then, if that overload resolution is ambiguous, the std::variant converting constructor call is ill-formed.
The note does not say that the variant<string, string> example was ill-formed because the type selected by overload resolution occurs twice in the list of alternatives. It says that the overload resolution itself was ambiguous (because the two types had equally viable constructors). This distinction is important. If this example were rejected after the overload resolution stage, an argument could be made that your code is well-formed since the top-level cv-qualifiers would be deleted from the parameter types, making all four overloads FUN(int) so that T_j = int. But since the note suggests a failure during overload resolution, that means your example is ambiguous (as the 4 signatures are equivalent) and this must be diagnosed.
This is rejected by trunk gcc and clang.
– T.C.
4 hours ago
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/4.0/"u003ecc by-sa 4.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%2f57925019%2fstdvariant-converting-constructor-doesnt-handle-const-volatile-qualifiers%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Yeah, this is just how functions work when you pass by value.
The function void foo(int) and the function void foo(const int) and the function void foo(volatile int) and the function void foo(const volatile int) are all the same function.
By extension, there is no distinction for your variant's converting constructor to make, and no meaningful way to use a variant whose alternatives differ only in their top-level cv-qualifier.
(Well, okay, you can emplace with an explicit template argument, as Marek shows, but why? To what end?)
[dcl.fct/5][..] After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. [..]
3
I'm just wondering why the code compiles. Shouldn't it, according to eel.is/c++draft/variant.ctor#12.sentence-2, produce some ambiguity error in case there are multiple viable overloads? Is the code well-formed or not? (Kind-of a relevant case is mentioned in this note: eel.is/c++draft/variant.ctor#17.note-1, though there are same types).
– Daniel Langr
8 hours ago
@DanielLangr IMO, there are no multiple viable overloads. There is only one converting constructor (after adjustment), possibly multiply-defined (though that's okay for templates). Not really sure whether this is all well-formed though.
– Lightness Races in Orbit
7 hours ago
@DanielLangr That note is interesting. Okay maybe it's not well-formed then :P
– Lightness Races in Orbit
7 hours ago
Not multiple overloads of the constructor. I meant multiple overloads of those imaginaryfunfunctions I linked.
– Daniel Langr
7 hours ago
add a comment
|
Yeah, this is just how functions work when you pass by value.
The function void foo(int) and the function void foo(const int) and the function void foo(volatile int) and the function void foo(const volatile int) are all the same function.
By extension, there is no distinction for your variant's converting constructor to make, and no meaningful way to use a variant whose alternatives differ only in their top-level cv-qualifier.
(Well, okay, you can emplace with an explicit template argument, as Marek shows, but why? To what end?)
[dcl.fct/5][..] After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. [..]
3
I'm just wondering why the code compiles. Shouldn't it, according to eel.is/c++draft/variant.ctor#12.sentence-2, produce some ambiguity error in case there are multiple viable overloads? Is the code well-formed or not? (Kind-of a relevant case is mentioned in this note: eel.is/c++draft/variant.ctor#17.note-1, though there are same types).
– Daniel Langr
8 hours ago
@DanielLangr IMO, there are no multiple viable overloads. There is only one converting constructor (after adjustment), possibly multiply-defined (though that's okay for templates). Not really sure whether this is all well-formed though.
– Lightness Races in Orbit
7 hours ago
@DanielLangr That note is interesting. Okay maybe it's not well-formed then :P
– Lightness Races in Orbit
7 hours ago
Not multiple overloads of the constructor. I meant multiple overloads of those imaginaryfunfunctions I linked.
– Daniel Langr
7 hours ago
add a comment
|
Yeah, this is just how functions work when you pass by value.
The function void foo(int) and the function void foo(const int) and the function void foo(volatile int) and the function void foo(const volatile int) are all the same function.
By extension, there is no distinction for your variant's converting constructor to make, and no meaningful way to use a variant whose alternatives differ only in their top-level cv-qualifier.
(Well, okay, you can emplace with an explicit template argument, as Marek shows, but why? To what end?)
[dcl.fct/5][..] After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. [..]
Yeah, this is just how functions work when you pass by value.
The function void foo(int) and the function void foo(const int) and the function void foo(volatile int) and the function void foo(const volatile int) are all the same function.
By extension, there is no distinction for your variant's converting constructor to make, and no meaningful way to use a variant whose alternatives differ only in their top-level cv-qualifier.
(Well, okay, you can emplace with an explicit template argument, as Marek shows, but why? To what end?)
[dcl.fct/5][..] After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. [..]
answered 8 hours ago
Lightness Races in OrbitLightness Races in Orbit
315k59 gold badges525 silver badges870 bronze badges
315k59 gold badges525 silver badges870 bronze badges
3
I'm just wondering why the code compiles. Shouldn't it, according to eel.is/c++draft/variant.ctor#12.sentence-2, produce some ambiguity error in case there are multiple viable overloads? Is the code well-formed or not? (Kind-of a relevant case is mentioned in this note: eel.is/c++draft/variant.ctor#17.note-1, though there are same types).
– Daniel Langr
8 hours ago
@DanielLangr IMO, there are no multiple viable overloads. There is only one converting constructor (after adjustment), possibly multiply-defined (though that's okay for templates). Not really sure whether this is all well-formed though.
– Lightness Races in Orbit
7 hours ago
@DanielLangr That note is interesting. Okay maybe it's not well-formed then :P
– Lightness Races in Orbit
7 hours ago
Not multiple overloads of the constructor. I meant multiple overloads of those imaginaryfunfunctions I linked.
– Daniel Langr
7 hours ago
add a comment
|
3
I'm just wondering why the code compiles. Shouldn't it, according to eel.is/c++draft/variant.ctor#12.sentence-2, produce some ambiguity error in case there are multiple viable overloads? Is the code well-formed or not? (Kind-of a relevant case is mentioned in this note: eel.is/c++draft/variant.ctor#17.note-1, though there are same types).
– Daniel Langr
8 hours ago
@DanielLangr IMO, there are no multiple viable overloads. There is only one converting constructor (after adjustment), possibly multiply-defined (though that's okay for templates). Not really sure whether this is all well-formed though.
– Lightness Races in Orbit
7 hours ago
@DanielLangr That note is interesting. Okay maybe it's not well-formed then :P
– Lightness Races in Orbit
7 hours ago
Not multiple overloads of the constructor. I meant multiple overloads of those imaginaryfunfunctions I linked.
– Daniel Langr
7 hours ago
3
3
I'm just wondering why the code compiles. Shouldn't it, according to eel.is/c++draft/variant.ctor#12.sentence-2, produce some ambiguity error in case there are multiple viable overloads? Is the code well-formed or not? (Kind-of a relevant case is mentioned in this note: eel.is/c++draft/variant.ctor#17.note-1, though there are same types).
– Daniel Langr
8 hours ago
I'm just wondering why the code compiles. Shouldn't it, according to eel.is/c++draft/variant.ctor#12.sentence-2, produce some ambiguity error in case there are multiple viable overloads? Is the code well-formed or not? (Kind-of a relevant case is mentioned in this note: eel.is/c++draft/variant.ctor#17.note-1, though there are same types).
– Daniel Langr
8 hours ago
@DanielLangr IMO, there are no multiple viable overloads. There is only one converting constructor (after adjustment), possibly multiply-defined (though that's okay for templates). Not really sure whether this is all well-formed though.
– Lightness Races in Orbit
7 hours ago
@DanielLangr IMO, there are no multiple viable overloads. There is only one converting constructor (after adjustment), possibly multiply-defined (though that's okay for templates). Not really sure whether this is all well-formed though.
– Lightness Races in Orbit
7 hours ago
@DanielLangr That note is interesting. Okay maybe it's not well-formed then :P
– Lightness Races in Orbit
7 hours ago
@DanielLangr That note is interesting. Okay maybe it's not well-formed then :P
– Lightness Races in Orbit
7 hours ago
Not multiple overloads of the constructor. I meant multiple overloads of those imaginary
fun functions I linked.– Daniel Langr
7 hours ago
Not multiple overloads of the constructor. I meant multiple overloads of those imaginary
fun functions I linked.– Daniel Langr
7 hours ago
add a comment
|
Note that you are creating copy of value. This means that const and volatile modifiers can be safely discarded. That is why template always deduces int.
You can force specific type using emplace.
See demo https://coliru.stacked-crooked.com/a/4dd054dc4fa9bb9a
2
Those warnings are a bit alarming - what do they mean?
– Lightness Races in Orbit
8 hours ago
@LightnessRacesinOrbit See here.emplacereturns the constructed value, andgccwarns that discarding that reference tovolatilereturn value does not perform a read on it. The warning goes away if you actually do read from the returned reference tovolatile: godbolt.org/z/F24Iea
– Max Langhof
8 hours ago
@MaxLanghof Got ya.
– Lightness Races in Orbit
7 hours ago
add a comment
|
Note that you are creating copy of value. This means that const and volatile modifiers can be safely discarded. That is why template always deduces int.
You can force specific type using emplace.
See demo https://coliru.stacked-crooked.com/a/4dd054dc4fa9bb9a
2
Those warnings are a bit alarming - what do they mean?
– Lightness Races in Orbit
8 hours ago
@LightnessRacesinOrbit See here.emplacereturns the constructed value, andgccwarns that discarding that reference tovolatilereturn value does not perform a read on it. The warning goes away if you actually do read from the returned reference tovolatile: godbolt.org/z/F24Iea
– Max Langhof
8 hours ago
@MaxLanghof Got ya.
– Lightness Races in Orbit
7 hours ago
add a comment
|
Note that you are creating copy of value. This means that const and volatile modifiers can be safely discarded. That is why template always deduces int.
You can force specific type using emplace.
See demo https://coliru.stacked-crooked.com/a/4dd054dc4fa9bb9a
Note that you are creating copy of value. This means that const and volatile modifiers can be safely discarded. That is why template always deduces int.
You can force specific type using emplace.
See demo https://coliru.stacked-crooked.com/a/4dd054dc4fa9bb9a
answered 8 hours ago
Marek RMarek R
15.5k3 gold badges30 silver badges81 bronze badges
15.5k3 gold badges30 silver badges81 bronze badges
2
Those warnings are a bit alarming - what do they mean?
– Lightness Races in Orbit
8 hours ago
@LightnessRacesinOrbit See here.emplacereturns the constructed value, andgccwarns that discarding that reference tovolatilereturn value does not perform a read on it. The warning goes away if you actually do read from the returned reference tovolatile: godbolt.org/z/F24Iea
– Max Langhof
8 hours ago
@MaxLanghof Got ya.
– Lightness Races in Orbit
7 hours ago
add a comment
|
2
Those warnings are a bit alarming - what do they mean?
– Lightness Races in Orbit
8 hours ago
@LightnessRacesinOrbit See here.emplacereturns the constructed value, andgccwarns that discarding that reference tovolatilereturn value does not perform a read on it. The warning goes away if you actually do read from the returned reference tovolatile: godbolt.org/z/F24Iea
– Max Langhof
8 hours ago
@MaxLanghof Got ya.
– Lightness Races in Orbit
7 hours ago
2
2
Those warnings are a bit alarming - what do they mean?
– Lightness Races in Orbit
8 hours ago
Those warnings are a bit alarming - what do they mean?
– Lightness Races in Orbit
8 hours ago
@LightnessRacesinOrbit See here.
emplace returns the constructed value, and gcc warns that discarding that reference to volatile return value does not perform a read on it. The warning goes away if you actually do read from the returned reference to volatile: godbolt.org/z/F24Iea– Max Langhof
8 hours ago
@LightnessRacesinOrbit See here.
emplace returns the constructed value, and gcc warns that discarding that reference to volatile return value does not perform a read on it. The warning goes away if you actually do read from the returned reference to volatile: godbolt.org/z/F24Iea– Max Langhof
8 hours ago
@MaxLanghof Got ya.
– Lightness Races in Orbit
7 hours ago
@MaxLanghof Got ya.
– Lightness Races in Orbit
7 hours ago
add a comment
|
My reading of the standard is that the code should be ill-formed due to ambiguity. It surprises me that both libstdc++ and libc++ appear to allow it.
Here's what [variant.ctor]/12 says:
Let
T_j be a type that is determined as follows: build an imaginary function FUN(T_i) for each alternative typeT_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t))defines the alternativeT_j which is the type of the contained value after construction.
So four functions are created: initially FUN(int), FUN(const int), FUN(volatile int), and FUN(const volatile int). These are all equivalent signatures, so they could not be overloaded with each other. This paragraph does not really specify what should happen if the overload set cannot actually be built. However, there is a note that strongly implies a particular interpretation:
[ Note:
variant<string, string> v("abc");
is ill-formed, as both alternative types have an equally viable constructor for the argument. —end note]
This note is basically saying that overload resolution cannot distinguish between string and string. In order for that to happen, overload resolution must be done even though the signatures are the same. The two FUN(string)s are not collapsed into a single function.
Note that overload resolution is allowed to consider overloads with identical signatures due to templates. For example:
template <class T> struct Id1 using type = T; ;
template <class T> struct Id2 using type = T; ;
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous
Here, there are two identical signatures of f, and both are submitted to overload resolution but neither is better than the other.
Going back to the Standard's example, it seems that the prescription is to apply the overload resolution procedure even if some of the overloads could not be overloaded with each other as ordinary function declarations. (If you want, imagine that they are all instantiated from templates.) Then, if that overload resolution is ambiguous, the std::variant converting constructor call is ill-formed.
The note does not say that the variant<string, string> example was ill-formed because the type selected by overload resolution occurs twice in the list of alternatives. It says that the overload resolution itself was ambiguous (because the two types had equally viable constructors). This distinction is important. If this example were rejected after the overload resolution stage, an argument could be made that your code is well-formed since the top-level cv-qualifiers would be deleted from the parameter types, making all four overloads FUN(int) so that T_j = int. But since the note suggests a failure during overload resolution, that means your example is ambiguous (as the 4 signatures are equivalent) and this must be diagnosed.
This is rejected by trunk gcc and clang.
– T.C.
4 hours ago
add a comment
|
My reading of the standard is that the code should be ill-formed due to ambiguity. It surprises me that both libstdc++ and libc++ appear to allow it.
Here's what [variant.ctor]/12 says:
Let
T_j be a type that is determined as follows: build an imaginary function FUN(T_i) for each alternative typeT_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t))defines the alternativeT_j which is the type of the contained value after construction.
So four functions are created: initially FUN(int), FUN(const int), FUN(volatile int), and FUN(const volatile int). These are all equivalent signatures, so they could not be overloaded with each other. This paragraph does not really specify what should happen if the overload set cannot actually be built. However, there is a note that strongly implies a particular interpretation:
[ Note:
variant<string, string> v("abc");
is ill-formed, as both alternative types have an equally viable constructor for the argument. —end note]
This note is basically saying that overload resolution cannot distinguish between string and string. In order for that to happen, overload resolution must be done even though the signatures are the same. The two FUN(string)s are not collapsed into a single function.
Note that overload resolution is allowed to consider overloads with identical signatures due to templates. For example:
template <class T> struct Id1 using type = T; ;
template <class T> struct Id2 using type = T; ;
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous
Here, there are two identical signatures of f, and both are submitted to overload resolution but neither is better than the other.
Going back to the Standard's example, it seems that the prescription is to apply the overload resolution procedure even if some of the overloads could not be overloaded with each other as ordinary function declarations. (If you want, imagine that they are all instantiated from templates.) Then, if that overload resolution is ambiguous, the std::variant converting constructor call is ill-formed.
The note does not say that the variant<string, string> example was ill-formed because the type selected by overload resolution occurs twice in the list of alternatives. It says that the overload resolution itself was ambiguous (because the two types had equally viable constructors). This distinction is important. If this example were rejected after the overload resolution stage, an argument could be made that your code is well-formed since the top-level cv-qualifiers would be deleted from the parameter types, making all four overloads FUN(int) so that T_j = int. But since the note suggests a failure during overload resolution, that means your example is ambiguous (as the 4 signatures are equivalent) and this must be diagnosed.
This is rejected by trunk gcc and clang.
– T.C.
4 hours ago
add a comment
|
My reading of the standard is that the code should be ill-formed due to ambiguity. It surprises me that both libstdc++ and libc++ appear to allow it.
Here's what [variant.ctor]/12 says:
Let
T_j be a type that is determined as follows: build an imaginary function FUN(T_i) for each alternative typeT_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t))defines the alternativeT_j which is the type of the contained value after construction.
So four functions are created: initially FUN(int), FUN(const int), FUN(volatile int), and FUN(const volatile int). These are all equivalent signatures, so they could not be overloaded with each other. This paragraph does not really specify what should happen if the overload set cannot actually be built. However, there is a note that strongly implies a particular interpretation:
[ Note:
variant<string, string> v("abc");
is ill-formed, as both alternative types have an equally viable constructor for the argument. —end note]
This note is basically saying that overload resolution cannot distinguish between string and string. In order for that to happen, overload resolution must be done even though the signatures are the same. The two FUN(string)s are not collapsed into a single function.
Note that overload resolution is allowed to consider overloads with identical signatures due to templates. For example:
template <class T> struct Id1 using type = T; ;
template <class T> struct Id2 using type = T; ;
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous
Here, there are two identical signatures of f, and both are submitted to overload resolution but neither is better than the other.
Going back to the Standard's example, it seems that the prescription is to apply the overload resolution procedure even if some of the overloads could not be overloaded with each other as ordinary function declarations. (If you want, imagine that they are all instantiated from templates.) Then, if that overload resolution is ambiguous, the std::variant converting constructor call is ill-formed.
The note does not say that the variant<string, string> example was ill-formed because the type selected by overload resolution occurs twice in the list of alternatives. It says that the overload resolution itself was ambiguous (because the two types had equally viable constructors). This distinction is important. If this example were rejected after the overload resolution stage, an argument could be made that your code is well-formed since the top-level cv-qualifiers would be deleted from the parameter types, making all four overloads FUN(int) so that T_j = int. But since the note suggests a failure during overload resolution, that means your example is ambiguous (as the 4 signatures are equivalent) and this must be diagnosed.
My reading of the standard is that the code should be ill-formed due to ambiguity. It surprises me that both libstdc++ and libc++ appear to allow it.
Here's what [variant.ctor]/12 says:
Let
T_j be a type that is determined as follows: build an imaginary function FUN(T_i) for each alternative typeT_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t))defines the alternativeT_j which is the type of the contained value after construction.
So four functions are created: initially FUN(int), FUN(const int), FUN(volatile int), and FUN(const volatile int). These are all equivalent signatures, so they could not be overloaded with each other. This paragraph does not really specify what should happen if the overload set cannot actually be built. However, there is a note that strongly implies a particular interpretation:
[ Note:
variant<string, string> v("abc");
is ill-formed, as both alternative types have an equally viable constructor for the argument. —end note]
This note is basically saying that overload resolution cannot distinguish between string and string. In order for that to happen, overload resolution must be done even though the signatures are the same. The two FUN(string)s are not collapsed into a single function.
Note that overload resolution is allowed to consider overloads with identical signatures due to templates. For example:
template <class T> struct Id1 using type = T; ;
template <class T> struct Id2 using type = T; ;
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous
Here, there are two identical signatures of f, and both are submitted to overload resolution but neither is better than the other.
Going back to the Standard's example, it seems that the prescription is to apply the overload resolution procedure even if some of the overloads could not be overloaded with each other as ordinary function declarations. (If you want, imagine that they are all instantiated from templates.) Then, if that overload resolution is ambiguous, the std::variant converting constructor call is ill-formed.
The note does not say that the variant<string, string> example was ill-formed because the type selected by overload resolution occurs twice in the list of alternatives. It says that the overload resolution itself was ambiguous (because the two types had equally viable constructors). This distinction is important. If this example were rejected after the overload resolution stage, an argument could be made that your code is well-formed since the top-level cv-qualifiers would be deleted from the parameter types, making all four overloads FUN(int) so that T_j = int. But since the note suggests a failure during overload resolution, that means your example is ambiguous (as the 4 signatures are equivalent) and this must be diagnosed.
answered 8 hours ago
BrianBrian
72.6k7 gold badges105 silver badges207 bronze badges
72.6k7 gold badges105 silver badges207 bronze badges
This is rejected by trunk gcc and clang.
– T.C.
4 hours ago
add a comment
|
This is rejected by trunk gcc and clang.
– T.C.
4 hours ago
This is rejected by trunk gcc and clang.
– T.C.
4 hours ago
This is rejected by trunk gcc and clang.
– T.C.
4 hours ago
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%2f57925019%2fstdvariant-converting-constructor-doesnt-handle-const-volatile-qualifiers%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
Note that
auto a = var_cwill discardconst, andauto b = var_vwill discardvolatileand this is source of your problems.intis always auto deduced when calling constructor.– Marek R
8 hours ago
@MarekR Auto deduced where? Variant's constructor uses a forwarding reference, where
constandvolatilequalifiers are preserved in a template parameter, IIRC (live demo). I believe the problem is with those imaginaryFUNfunctions that pass by value.– Daniel Langr
8 hours ago