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;








6















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.










share|improve this question


























  • 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


















6















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.










share|improve this question


























  • 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














6












6








6








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.










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 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


















  • 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

















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













3 Answers
3






active

oldest

votes


















6
















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. [..]







share|improve this answer




















  • 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 imaginary fun functions I linked.

    – Daniel Langr
    7 hours ago


















4
















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






share|improve this answer




















  • 2





    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











  • @MaxLanghof Got ya.

    – Lightness Races in Orbit
    7 hours ago


















3
















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 type T_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative T_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.






share|improve this answer

























  • This is rejected by trunk gcc and clang.

    – T.C.
    4 hours ago













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
);



);














draft saved

draft discarded
















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









6
















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. [..]







share|improve this answer




















  • 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 imaginary fun functions I linked.

    – Daniel Langr
    7 hours ago















6
















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. [..]







share|improve this answer




















  • 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 imaginary fun functions I linked.

    – Daniel Langr
    7 hours ago













6














6










6









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. [..]







share|improve this answer













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. [..]








share|improve this answer












share|improve this answer



share|improve this answer










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 imaginary fun functions I linked.

    – Daniel Langr
    7 hours ago












  • 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 imaginary fun functions 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













4
















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






share|improve this answer




















  • 2





    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











  • @MaxLanghof Got ya.

    – Lightness Races in Orbit
    7 hours ago















4
















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






share|improve this answer




















  • 2





    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











  • @MaxLanghof Got ya.

    – Lightness Races in Orbit
    7 hours ago













4














4










4









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






share|improve this answer













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







share|improve this answer












share|improve this answer



share|improve this answer










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. 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












  • 2





    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











  • @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











3
















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 type T_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative T_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.






share|improve this answer

























  • This is rejected by trunk gcc and clang.

    – T.C.
    4 hours ago















3
















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 type T_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative T_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.






share|improve this answer

























  • This is rejected by trunk gcc and clang.

    – T.C.
    4 hours ago













3














3










3









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 type T_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative T_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.






share|improve this answer













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 type T_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative T_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.







share|improve this answer












share|improve this answer



share|improve this answer










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

















  • 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


















draft saved

draft discarded















































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.




draft saved


draft discarded














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





















































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