Re: [boost] [1.33.0] VC 7.1 vs. SFINAE/type traits

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Thomas, On 8/16/05, Thomas Witt <witt@acm.org> wrote: this:
Thanks for sending this fascinating example. After doing a bit of my own exploration with gcc 4.0 I must agree with you. If I understand the ramifications correctly, if gcc 4.0 correctly disallows instantiating templates upon unnamed enum types, then SFINAE cannot be safely used for operators that may also happen to exist for (anonymous) enums. In the example above, even if we replace is_enum with a more restricted metafunction, the problem will not go away, since any instantiation would seem to be prohibited. Worse still, though it is easy enough to avoid calling the operator with an anonymous enum, I must also avoid files that might use operators on other unrelated anonymous enums, lest the compiler try to illegally instantiate the metafunction. So the big question is, "Is there no known way of safely testing whether a type is an anonymous enum?" Thanks, Mat -----BEGIN PGP SIGNATURE----- Version: PGP Desktop 9.0.2 (Build 2424) iQA/AwUBQwL96RIZBaoznf65EQKf6wCcDyhiiRlQBMQ5NBISeTiQ0rnVEuwAn1y6 D54sfcwOgIvouUhH6QWtzLgv =f75H -----END PGP SIGNATURE-----

-----Original Message----- From: boost-bounces@lists.boost.org
So the big question is, "Is there no known way of safely testing whether a type is an anonymous enum?"
Hi Mat. There shouldn't be a way to legally get it into a template to begin with. Template parameters cannot be bound to unnamed types. In other words, there is no context in which you could have an anonymous enum without already knowing that it is anonymous. Regards, Paul Mensonides

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Paul, Good to hear from you. Thanks for the reply. - --On Wednesday, August 17, 2005 2:15 AM -0700 Paul Mensonides <pmenso57@comcast.net> wrote:
Here is a simplified version of the context in which the problem seems to crop up. //---- operator.hpp template <class T> struct is_foo {/*...*/}; // some appropriate compile time test template <class T> typename boost::enable_if<boost::is_foo<T>, T>::type operator| (T a, T b); //---- client.cpp #include "operator.hpp" So far things are ok. But now suppose that I am using gcc 4.0 the client later includes a new header system_header.hpp. //---system_header.hpp enum { kCaseInsenstiveMask = 1L << 1, kReverseOrderMask = 1L << 2, kOverrideMask = 1L << 3}; enum { kTheWorks = kCaseInsenstiveMask | kReverseOrderMask | kOverrideMask }; That is: 1) If gcc 4.0 is correct, then I currently conclude that operator.hpp should break in the presence of system_header.hpp 2) I would like to "repair" operator| , and to make it robust in the presence of constructs like those in system_header.hpp 3) I asked whether anonmyous enum detection was possible in support of goal 2. - Mat -----BEGIN PGP SIGNATURE----- Version: PGP Desktop 9.0.2 (Build 2424) iQA/AwUBQwMH1BIZBaoznf65EQKzpwCgyEqGMd/EX579OT/Pbw0qpR3+rc4An2y7 C0vBBb00of44HRz7TtP33Ftv =Wb+k -----END PGP SIGNATURE-----

Peter Dimov wrote:
This is very disturbing; it basically breaks all SFINAE'd operators. If this is what the language currently mandates, it needs to be fixed.
As Mike Miller pointed out on the core reflector, this is issue #488: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#488 and the tentative resolution is to treat the use of a type without linkage as a deduction failure.

The standard doesn't say anything about this directly, AFAICT. Same thing with types with internal linkage (such as function-local types). Neither is in the explicit SFINAE list (though it should be). So, I don't know for sure if gcc is correct.
2) I would like to "repair" operator| , and to make it robust in the presence of constructs like those in system_header.hpp
3) I asked whether anonmyous enum detection was possible in support of goal 2.
There is nothing that you can do here. If the correct behavior is to have type deduction (or substitution) fail for any anonymous type or type with internal linkage, then it would be posssible to detect whether something was an anonymous type, but only directly. I.e. you couldn't package it in a metafunction. OTOH, you wouldn't need to detect it, if that was the case. [Diatribe: I've mentioned this before (and I know you're referring to "system header" here), but use of enumerations has to be done carefully because they are unique types. Because they are unique types, operators can be overloaded on them--including template operators--which can turn constant expressions into runtime expressions. They can also cause unnecessary template bloat. Const variables are safer (or static const inside classes).] Getting back to the issue at hand... Basically I think that gcc is wrong--bear with me while I explain why I think that. Before overload resolution occurs, entities are looked up (via normal lookup and ADL). All names that denote function templates undergo argument deduction--which may fail (SFINAE). All normal functions and all templates where argument deduction succeeds become part of the candidate set (including functions with too few or too many arguments for the call provided they somehow get through argument deduction). Overload resolution then occurs on that candidate set. (Overload resolution discards those functions that require too many or too few arguments.) This is what strictly happens according to the standard. (A lot of compilers, if not all, cheat heavily in this area by blurring together argument deduction and overload resolution.) Basically, what it comes down to is that this... template<class T> struct A { }; template<class T> A<T> operator|(A<T>, A<T>); ...is no different than... template<class T> T operator|(T, T); ...as far as argument deduction is concerned. Even though the compiler can cheat and know that an enum cannot match A<T>, it is supposed to attempt argument deduction. The problem here is that there is no way for argument deduction to succeed with an anonymous type because an anonymous type cannot be bound to a template argument--meaning that argument deduction must fail before it even tries. If gcc is correct, any visible template operator| at all should cause an error in the enumeration initializers--which is completely ridiculous and definitely not the intended behavior. Therefore, I think that gcc is wrong. Given that this is the case with gcc (regardless of whether it is correct), I don't think there is any way that you can protect your operator|--by the time you can do anything with T, it is too late. The only solution, AFAICS, is to name the enumeration type, which will cause SFINAE to kick in and discard your operator| when it does the enumerator initializers. Regards, Paul Mensonides

"Paul Mensonides" <pmenso57@comcast.net> writes:
Mat, Maybe I am misunderstanding what Paul is saying here, but it seems to me if it argument deduction was currently supposed to fail when passed a type without linkage, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#488 wouldn't exist. -- Dave Abrahams Boost Consulting www.boost-consulting.com

From: David Abrahams <dave@boost-consulting.com>
Doesn't the issue suggest that what is currently supposed to happen is vague, so one can only determine empirically what a given compiler will do? -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

From: David Abrahams <dave@boost-consulting.com>
I understand now. Paul was saying argument deduction must fail and you were saying that it isn't clear that failed deduction is the mandated behavior. -- Rob Stewart stewart@sig.com Software Engineer http://www.sig.com Susquehanna International Group, LLP using std::disclaimer;

Hi again, "Paul Mensonides" <pmenso57@comcast.net> wrote
FWIW This compiles in VC7.1 notwithstanding that the unnamed enum gives the wrong result, which of course is probably the problem in the original code as Thomas Witt says. #include <iostream> #include "boost/type_traits/is_enum.hpp" #include <boost/typeof/typeof.hpp> enum {X,Y}; enum named{XX,YY}; int main() { bool b0 = boost::is_enum<BOOST_TYPEOF(X) >::value; std::cout << b0 <<'\n'; bool b1 = boost::is_enum<BOOST_TYPEOF(XX) >::value; std::cout << b1 <<'\n'; } output: 0 1 Andy Little
participants (6)
-
Andy Little
-
David Abrahams
-
Mat Marcus
-
Paul Mensonides
-
Peter Dimov
-
Rob Stewart