
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Mat Marcus
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
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