[type traits extension] test for const volatile& as return type

I am still working on the extension of the type trait library to operator detection. I was pretty happy having now all my tests passing with g++, in particular for all possible combinations of const, volatile and reference. I have now some troubles with the intel compiler. The following program reproduces a part of the internal implementation of the operator traits (with T defined below): #include <iostream> void g(T) { std::cout<<"g(T)\n"; } void g(...) { std::cout<<"g(...)\n"; } int main() { int i(0); g(i--); return 0; } This is what I get with g++ 4.6.0 for different values of T: - int&: g(...) - const int&: g(T) - volatile int&: g(...) - const volatile int&: g(...) This is what I get with intel 11.1: - int&: g(...) - const int&: g(T) - volatile int&: g(...) - const volatile int&: error: initial value of reference to const volatile must be an lvalue g++ is just fine for what I want to do but intel issues an error if T is "const volatile&" so that the program cannot compile and the g(...) is not chosen. Does anybody understands if this is the right behaviour according to the standard or if g++ is right chosing g(...)? Cheers, Frédéric

On 7/5/2011 3:50 PM, Frédéric Bron wrote:
I am still working on the extension of the type trait library to operator detection. I was pretty happy having now all my tests passing with g++, in particular for all possible combinations of const, volatile and reference.
I have now some troubles with the intel compiler. The following program reproduces a part of the internal implementation of the operator traits (with T defined below):
#include<iostream> void g(T) { std::cout<<"g(T)\n"; }
template <class T> void g(T) { std::cout<<"g(T)\n"; } ?
void g(...) { std::cout<<"g(...)\n"; } int main() { int i(0); g(i--); return 0; }
This is what I get with g++ 4.6.0 for different values of T: - int&: g(...) - const int&: g(T) - volatile int&: g(...) - const volatile int&: g(...)
This is what I get with intel 11.1: - int&: g(...) - const int&: g(T) - volatile int&: g(...) - const volatile int&: error: initial value of reference to const volatile must be an lvalue
g++ is just fine for what I want to do but intel issues an error if T is "const volatile&" so that the program cannot compile and the g(...) is not chosen. Does anybody understands if this is the right behaviour according to the standard or if g++ is right chosing g(...)?
I think you need to reproduce your exact case. Your code above can not tell anyone what is actually going on. I ran into Intel 11.1 and 12.0 problems in TTI if it makes you feel any better, but that does not mean Intel is wrong and gcc is right in your case. I am sorry I do not have Intel to test. I know they give a 30-day trial but after that one has to pay and it is not that inexpensive.

On Tue, Jul 5, 2011 at 7:19 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/5/2011 3:50 PM, Frédéric Bron wrote:
[...]
This is what I get with g++ 4.6.0 for different values of T:
- int&: g(...) - const int&: g(T) - volatile int&: g(...) - const volatile int&: g(...)
This is what I get with intel 11.1: - int&: g(...) - const int&: g(T) - volatile int&: g(...) - const volatile int&: error: initial value of reference to const volatile must be an lvalue
g++ is just fine for what I want to do but intel issues an error if T is "const volatile&" so that the program cannot compile and the g(...) is not chosen. Does anybody understands if this is the right behaviour according to the standard or if g++ is right chosing g(...)?
I think you need to reproduce your exact case. Your code above can not tell anyone what is actually going on.
I'm not sure that's entirely fair; it just may not have been presented in the clearest way. The crux of the matter is, unless I'm missing something, that GCC prefers binding an rvalue of type int to f(...) rather than f(const volatile int&), while Intel just barfs when offered those alternatives. My limited understanding of overload resolution and function argument binding says that GCC is right, and there should be no error, but I have no authoritative sources to back that up, so don't take my word for it. On the bright side, at least Intel doesn't ICE like MSVC does on some even relatively mild TMP :) - Jeff

template <class T> void g(T) { std::cout<<"g(T)\n"; } ?
I cannot use that because I want in that particular case g(...) be chosen.
I think you need to reproduce your exact case. Your code above can not tell anyone what is actually going on.
I maybe forgot to say that I want g(...) to be chosen for T=const int&. Frédéric

2011/7/5 Frédéric Bron <frederic.bron@m4x.org>
template <class T> void g(T) { std::cout<<"g(T)\n"; } ?
I cannot use that because I want in that particular case g(...) be chosen.
I think you need to reproduce your exact case. Your code above can not tell anyone what is actually going on.
I maybe forgot to say that I want g(...) to be chosen for T=const int&.
I don't think that's gonna happen; rvalues (at least with no cv qualifiers) are always bindable to reference-to-consts (but, evidently, not reference-to-const-volatiles...), and certainly preferable to (...). - Jeff

I maybe forgot to say that I want g(...) to be chosen for T=const int&. I don't think that's gonna happen; rvalues (at least with no cv qualifiers) are always bindable to reference-to-consts (but, evidently, not reference-to-const-volatiles...), and certainly preferable to (...).
Sorry, my fault. I wanted to say const volatile int&!!! Frédéric

On 7/6/2011 12:44 AM, Frédéric Bron wrote:
template<class T> void g(T) { std::cout<<"g(T)\n"; } ?
I cannot use that because I want in that particular case g(...) be chosen.
I think you need to reproduce your exact case. Your code above can not tell anyone what is actually going on.
I maybe forgot to say that I want g(...) to be chosen for T=const int&.
I apologize, Frederic. I could not tell what you meant as your example in your OP.

This is what I get with g++ 4.6.0 for different values of T: - int&: g(...) - const int&: g(T) - volatile int&: g(...) - const volatile int&: g(...)
This is what I get with intel 11.1: - int&: g(...) - const int&: g(T) - volatile int&: g(...) - const volatile int&: error: initial value of reference to const volatile must be an lvalue
And here is what msvc 10 does: - int&: g(...) - const int&: g(T) - volatile int&: g(...) - const volatile int&: g(...) so the same as g++ (no compilation error). Frédéric

g++ is just fine for what I want to do but intel issues an error if T is "const volatile&" so that the program cannot compile and the g(...) is not chosen. Does anybody understands if this is the right behaviour according to the standard or if g++ is right chosing g(...)?
No idea, but if you have a test case I'm able to file bug reports to Intel. John.

No idea, but if you have a test case I'm able to file bug reports to Intel.
Thank you, because I have not been able to find the place where to file a bug... The following code fails to compile with icpc 12.0.4 20110427. The error is: initial value of reference to const volatile must be an lvalue I think it should choose g(...) instead of g(const volatile int&) and there should be no error then. This works fine if the argument is of type int& or volatile int&. Regards, Frédéric #include <iostream> void g(const volatile int&) { std::cout<<"g(T)\n"; } void g(...) { std::cout<<"g(...)\n"; } int main() { int i(0); g(i--); return 0; }

Frédéric Bron <frederic.bron <at> m4x.org> writes:
<snip> g++ is just fine for what I want to do but intel issues an error if T is "const volatile&" so that the program cannot compile and the g(...) is not chosen. Does anybody understands if this is the right behaviour according to the standard or if g++ is right chosing g(...)?
By my reading, the Intel compiler is correct in refusing to compile. Quoting §8.5.3/5 from the C++03 standard:
A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows: - If the initializer expression - [1] is an lvalue (but is not a bit-field), and "cv1 T1" is reference- compatible with "cv2 T2," or - [2] has a class type (i.e., T2 is a class type) and can be implicitly converted to an lvalue of type "cv3 T3," where "cv1 T1" is reference- compatible with "cv3 T3" (this conversion is selected by enumerating the applicable conversion functions and choosing the best one through overload resolution), then <snip...> - [3] Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const).
If I interpret this correctly, - [1] is not applicable because you're passing an rvalue - [2] is not applicable because int is not a class type - [3] specifically disallows volatile Obviously the wording in the C++0x FDIS differs, but still ultimately disallows this particular scenario. That said, I'm inclined to say that GCC and MSVC are in the wrong here, not Intel.

On Thu, Jul 7, 2011 at 4:27 PM, Adam Merz <adammerz@hotmail.com> wrote:
Frédéric Bron <frederic.bron <at> m4x.org> writes:
<snip> g++ is just fine for what I want to do but intel issues an error if T is "const volatile&" so that the program cannot compile and the g(...) is not chosen. Does anybody understands if this is the right behaviour according to the standard or if g++ is right chosing g(...)?
By my reading, the Intel compiler is correct in refusing to compile. Quoting §8.5.3/5 from the C++03 standard:
A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows: - If the initializer expression - [1] is an lvalue (but is not a bit-field), and "cv1 T1" is reference- compatible with "cv2 T2," or - [2] has a class type (i.e., T2 is a class type) and can be implicitly converted to an lvalue of type "cv3 T3," where "cv1 T1" is reference- compatible with "cv3 T3" (this conversion is selected by enumerating the applicable conversion functions and choosing the best one through overload resolution), then <snip...> - [3] Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const).
If I interpret this correctly, - [1] is not applicable because you're passing an rvalue - [2] is not applicable because int is not a class type - [3] specifically disallows volatile
Obviously the wording in the C++0x FDIS differs, but still ultimately disallows this particular scenario.
That said, I'm inclined to say that GCC and MSVC are in the wrong here, not Intel.
By this logic, void f(int&); void f(...) f(i++) should also fail to compile, no? I would think that the failure of an int rvalue to initialize either an int& or an int const volatile & would simply drop that overload from consideration. - Jeff

Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung <at> gmail.com> writes:
<snip> By this logic,
void f(int&); void f(...) f(i++)
should also fail to compile, no?
I would think that the failure of an int rvalue to initialize either an int& or an int const volatile & would simply drop that overload from consideration.
I was so focused on whether it would be legal to call `void g(int const volatile&)` with an int rvalue that I completely forgot about the ellipsis overload. :-P I agree with you completely -- in this context `void g(T)` should be discarded during overload resolution and `void g(...)` should be called. Forgive my oversight -- this is definitely a bug in the Intel compiler.

Adam Merz <adammerz <at> hotmail.com> writes:
Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung <at> gmail.com> writes:
<snip> By this logic,
void f(int&); void f(...) f(i++)
should also fail to compile, no?
I would think that the failure of an int rvalue to initialize either an int& or an int const volatile & would simply drop that overload from consideration.
I was so focused on whether it would be legal to call `void g(int const volatile&)` with an int rvalue that I completely forgot about the ellipsis overload. :-P I agree with you completely -- in this context `void g(T)` should be discarded during overload resolution and `void g(...)` should be called. Forgive my oversight -- this is definitely a bug in the Intel compiler.
I take that back -- the Intel compiler is right, GCC and MSVC are wrong. My earlier post focused on whether calling `void g(int const volatile&)` with an int rvalue would be legal (it isn't), but that was partially misplaced -- the relevant part of the standard is overload resolution. Starting from the top, quoting the C++03 standard, §13.3.3.1/3:
A well-formed implicit conversion sequence is one of the following forms: - a standard conversion sequence, - a user-defined conversion sequence, or - an ellipsis conversion sequence.
§13.3.3.2/2:
When comparing the basic forms of implicit conversion sequences - a standard conversion sequence is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and - a user-defined conversion sequence is a better conversion sequence than an ellipsis conversion sequence.
§13.3.3.1.4 repeats multiple times that for the case where the parameter type is a reference, that a standard conversion is taking place; however I won't quote all that here. So with the overloads: void g(int const volatile&); void g(...); When calling g with an int rvalue, the compiler must select the `void g(int const volatile&)` overload because "a standard conversion sequence is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence". Only then does the compiler decide that calling `void g(int const volatile&)` with an int rvalue is illegal. And this is not without precendence -- quoting §13.3.3.1.4/4:
Other restrictions on binding a reference to a particular argument do not affect the formation of a standard conversion sequence, however. [Example: a function with a "reference to int" parameter can be a viablecandidate even if the corresponding argument is an int bit-field. The formation of implicit conversion sequences treats the int bit-field as an int lvalue and finds an exact match with the parameter. If the function is selected by overload resolution, the call will nonetheless be ill-formed because of the prohibition on binding a non-const reference to a bit-field.]
Summary: Intel *is correct* according to the standarde, but GCC and MSVC take the sane/practical route.

On Thu, Jul 7, 2011 at 8:00 PM, Adam Merz <adammerz@hotmail.com> wrote: [...]
My earlier post focused on whether calling `void g(int const volatile&)` with an int rvalue would be legal (it isn't), but that was partially misplaced -- the relevant part of the standard is overload resolution.
Starting from the top, quoting the C++03 standard, §13.3.3.1/3:
A well-formed implicit conversion sequence is one of the following forms: - a standard conversion sequence, - a user-defined conversion sequence, or - an ellipsis conversion sequence.
§13.3.3.2/2:
When comparing the basic forms of implicit conversion sequences - a standard conversion sequence is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and - a user-defined conversion sequence is a better conversion sequence than an ellipsis conversion sequence.
§13.3.3.1.4 repeats multiple times that for the case where the parameter type is a reference, that a standard conversion is taking place; however I won't quote all that here.
So with the overloads:
void g(int const volatile&); void g(...);
When calling g with an int rvalue, the compiler must select the `void g(int const volatile&)` overload because "a standard conversion sequence is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence". Only then does the compiler decide that calling `void g(int const volatile&)` with an int rvalue is illegal. And this is not without precendence -- quoting §13.3.3.1.4/4:
Other restrictions on binding a reference to a particular argument do not affect the formation of a standard conversion sequence, however. [Example: a function with a "reference to int" parameter can be a viablecandidate even if the corresponding argument is an int bit-field. The formation of implicit conversion sequences treats the int bit-field as an int lvalue and finds an exact match with the parameter. If the function is selected by overload resolution, the call will nonetheless be ill-formed because of the prohibition on binding a non-const reference to a bit-field.]
Summary: Intel *is correct* according to the standarde, but GCC and MSVC take the sane/practical route.
What is a "standard conversion sequence"? [ I don't have a copy of the standard; if it's freely available on the internet somewhere, freely point me in the right direction :) ] - Jeff

Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung <at> gmail.com> writes:
<snip> What is a "standard conversion sequence"?
It's one of three classifications of conversion sequences; it would take copy/pasting most of §13.3.3.1 to define fully. The important part is that it's a better overload candidate than an ellipsis conversion sequence.
[ I don't have a copy of the standard; if it's freely available on the internet somewhere, freely point me in the right direction :) ]
It's ~$450 USD from the ISO committee if I recall correctly, but I can't imagine it would be that hard to find...

On Fri, Jul 08, 2011 at 04:41:05AM +0000, Adam Merz wrote:
Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung <at> gmail.com> writes:
[ I don't have a copy of the standard; if it's freely available on the internet somewhere, freely point me in the right direction :) ]
It's ~$450 USD from the ISO committee if I recall correctly, but I can't imagine it would be that hard to find...
I thought that was a typo at first, considering that the dead-tree version of 14882:2003 is around fifty bucks at Amazon. The FDIS (n3290) used to be available as PDF, but is behind some kind of password prompt nowadays sadly. Personally I consider it madness that the authorative specification for a major language is locked down behind some serious paywalls. -- Lars Viklund | zao@acc.umu.se

On Fri, Jul 08, 2011 at 08:12:52AM +0200, Lars Viklund wrote:
On Fri, Jul 08, 2011 at 04:41:05AM +0000, Adam Merz wrote:
Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung <at> gmail.com> writes:
[ I don't have a copy of the standard; if it's freely available on the internet somewhere, freely point me in the right direction :) ]
It's ~$450 USD from the ISO committee if I recall correctly, but I can't imagine it would be that hard to find...
I thought that was a typo at first, considering that the dead-tree version of 14882:2003 is around fifty bucks at Amazon.
Sorry for the multi-post, but ANSI has the PDF for $30, which is a much more reasonable price. -- Lars Viklund | zao@acc.umu.se

On Thu, Jul 7, 2011 at 9:41 PM, Adam Merz <adammerz@hotmail.com> wrote:
Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung <at> gmail.com> writes:
<snip> What is a "standard conversion sequence"?
It's one of three classifications of conversion sequences; it would take copy/pasting most of §13.3.3.1 to define fully. The important part is that it's a better overload candidate than an ellipsis conversion sequence.
Well arguably just as important is that there *is* a standard conversion sequence from an int to int const volatile &. Just to confirm, on MSVC9, the following compiles: int f(int const volatile &); //void f(...); // * int g(); int main(int argc, char* argv[]) { sizeof(f(g())); } ...but if you uncomment line *, error C2070: 'void': illegal sizeof operand So, right, MSVC (and, evidently, GCC) are preferring the (...) overload to (int const volatile &), even though the (int const volatile &) can legitimately be bound to the int rvalue. Maybe some compiler workarounds will have to introduced into these extended type traits to deal with const volatile & return types...
[ I don't have a copy of the standard; if it's freely available on the
internet somewhere, freely point me in the right direction :) ]
It's ~$450 USD from the ISO committee if I recall correctly, but I can't imagine it would be that hard to find...
Yes, this is what I remember running into some time ago; a google search now directed me to Herb Sutter's blog, which directed me to the ANSI page selling the pdf for $30, which is (almost) free relative to $450 (and within my student budget). But now with C++0x available on the horizon... - Jeff

I take that back -- the Intel compiler is right, GCC and MSVC are wrong.
void g(int const volatile&); void g(...);
When calling g with an int rvalue, the compiler must select the `void g(int const volatile&)` overload because "a standard conversion sequence is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence". Only then does the compiler decide that calling `void g(int const volatile&)` with an int rvalue is illegal. And this is not without precendence -- quoting §13.3.3.1.4/4:
Other restrictions on binding a reference to a particular argument do not affect the formation of a standard conversion sequence, however. [Example: a function with a "reference to int" parameter can be a viablecandidate even if the corresponding argument is an int bit-field. The formation of implicit conversion sequences treats the int bit-field as an int lvalue and finds an exact match with the parameter. If the function is selected by overload resolution, the call will nonetheless be ill-formed because of the prohibition on binding a non-const reference to a bit-field.]
Summary: Intel *is correct* according to the standarde, but GCC and MSVC take the sane/practical route.
I am not sure of your interpretation. Here are other parts of the standard: § 13.3.2 Viable functions: "for F to be a viable function, there shall exist for each argument an implicit conversion sequence (13.3.3.1) that converts that argument to the corresponding parameter of F. If the parameter has reference type, the implicit conversion sequence includes the operation of binding the reference, and the fact that a reference to non-const cannot be bound to an rvalue can affect the viability of the function (see 13.3.3.1.4)." § 13.3.3.1.4/3: "A standard conversion sequence cannot be formed if it requires binding a reference to non-const to an rvalue (except when binding an implicit object parameter; see the special rules for that case in 13.3.1). [Note: this means, for example, that a candidate function cannot be a viable function if it has a non-const reference parameter (other than the implicit object parameter) and the corresponding argument is a temporary or would require one to be created to initialize the reference (see 8.5.3). ]" So the question is: is "const volatile &" a "reference to non-const"? Or does "reference to non-const" mean exaclty T const & (excluding additionnal volatile qualifier) or not? If T const volatile & is considered as "reference to non-const" then Intel is wrong. If not, Intel is right. §8.5.3/5 you quoted earlier suggests that "reference to non-const" is only T const & which would mean that Intel is wrong: "A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows: — If the initializer expression — is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or — has a class type (i.e., T2 is a class type) and ... — Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const)." Frédéric

Frédéric Bron wrote:
Here are other parts of the standard: § 13.3.2 Viable functions: "for F to be a viable function, there shall exist for each argument an implicit conversion sequence (13.3.3.1) that converts that argument to the corresponding parameter of F. If the parameter has reference type, the implicit conversion sequence includes the operation of binding the reference, and the fact that a reference to non-const cannot be bound to an rvalue can affect the viability of the function (see 13.3.3.1.4)."
§ 13.3.3.1.4/3: "A standard conversion sequence cannot be formed if it requires binding a reference to non-const to an rvalue (except when binding an implicit object parameter; see the special rules for that case in 13.3.1). [Note: this means, for example, that a candidate function cannot be a viable function if it has a non-const reference parameter (other than the implicit object parameter) and the corresponding argument is a temporary or would require one to be created to initialize the reference (see 8.5.3). ]"
So the question is: is "const volatile &" a "reference to non- const"?
Were you awake into the wee hours again? "Reference to non-const" means T & or T volatile &. "const" can't be in the type.
Or does "reference to non-const" mean exaclty T const & (excluding additionnal volatile qualifier) or not?
T const & is a "reference to const" so it cannot be a "reference to non-const."
If T const volatile & is considered as "reference to non-const" then Intel is wrong.
It is not reference to non-const.
If not, Intel is right.
OK, assuming there's nothing else that bears on the situation. (I haven't studied your problem, so I can't say.)
§8.5.3/5 you quoted earlier suggests that "reference to non- const" is only T const & which would mean that Intel is wrong:
Did you mean to write "reference to const?" _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.
participants (7)
-
Adam Merz
-
Edward Diener
-
Frédéric Bron
-
Jeffrey Lee Hellrung, Jr.
-
John Maddock
-
Lars Viklund
-
Stewart, Robert