----- Original Message ----- From: "Daniel Frey" <[EMAIL PROTECTED]>
Terje Slettebų wrote: > > > Just to make sure: Do you "vote" in favor of enums? I have seen problems > > with 'static const ...', but I have never seen problems with enums > > (although they theoretically exist). > > Not just theoretically. As mentioned in an earlier posting in this thread, > BCC doesn't work well with enums, but it does work with static const. Also, > there's the issues pointed out by Paul Mensonides, due to the fact that > enums are UDTs. > Sorry, I should have been more exact. Of course we can only choose a > "default" implementation. If a compiler is buggy, we need to work-around > anyway. The drawback of enums, mentioned by Paul, is the one I call > theoretically, as I have never found this to be a problem in the real > world (well, in *my* real world :). A problem like this can be *created* > but special code which I have yet to see in "normal" programs. OTOH I > have already seen a colleage wondering about link-failures due to > passing a 'static const int ...' to a function which took a 'const T&'. > I consider this much more likely to happen in daily programming, YMMV :) The lack of problems in the "real world" is partially because compilers don't implement enumerations correctly. Even Comeau C++ gets this wrong (as in the small code sample I sent earlier in this thread). The problems associated with the use of enum are problems that cannot be directly avoided. They break encapsulation, and it is not the library's business whether a user does or doesn't do something that can affect the library internally but shouldn't. This is precisely the reason that macros are so derided--they cut across encapsulation boundaries. This reference problem, incidently, comes primarily from arguments to functions that are templated--either by themselves or as members of a class template. Otherwise, it should be a non-issue because there is no good reason to pass an integral constant by reference to const. This problem is typically bypassed by using enumeration values instead. However, this raises another interesting problem. Many enumerations used for this purpose are unnamed: template<class T> struct is_ptr { enum { value = false }; }; template<class T> struct is_ptr<T*> { enum { value = true }; }; This has its own problem in the same templated "pass by reference" situation. Namely, a template type argument cannot refer to an unnamed type: template<class T> T func(const T& ref) { return ref; } int main() { func( is_ptr<int*>::value ); // error: argument deduction yields unnamed type return 0; } The only way to solve this problem is to make a name for every enumeration type, rather than just the enumerators. However, this has its own associated problems--not even counting the annoyance of having to name the enumeration. It can cause code bloat by instantiated templates: template<class T> class A { // ... enum val_t { value = /* ??? */ }; }; template<class T> class B { // ... enum val_t { value = /* ??? */ }; }; template<class T> void f(const T& ref) { // ... return; } int main() { // two unique instantiations of 'f'... f(A<int>::value); f(B<int>::value); return 0; } Unfortunately, as it is now, because compilers don't support enumerations and static constants properly, we have a limitation that says "this value cannot be used as anything but a constant value, not even as a function argument of any kind without a user-specified cast at the call site." The reason is simple, unless we use only 'static const int' or enum (depending on the compiler) overload results can change from platform to platform. This makes the current mechanism very delicate--the only thorough solution is to cast the enumerator to int, bool, or whatever before any arithmetic, since there is no way that user code can break it. There are strengths to both enumerations and static constants. Given a perfect world (of compilers that is), enumerations only have two advantages: 1) they are syntactically easier to specify, and 2) they are pure values, i.e. built-in temporaries, as far as const& is concerned. Enumerations have downsides as well: 1) they are user-defined types, and therefore, user code can change integral constant expressions inside the library to runtime expressions, which has the potential to break the entire library, 2) they are not strongly typed in a way that helps distinguish the purpose of the value, and 3) they are uniquely typed, which has the potential to cause code bloat. Static constants, on the other hand, have couple of advantages also: 1) they are strongly typed, which conveys meaning and overloading that matches that meaning, and 2) they are encapsulated so as not to interfere with the rest of the language. The downside of static constants is if used as an actual object (i.e. address), they must have an external definition, which is "inconvenient." ;) I still think the benefits of static constants, despite the minor inconvenience, outweigh the benefits of enumerations, but, at this point, the argument is moot, since compilers don't support either properly. This is a pity because some of the most useful metaprogramming code is that which is mixed into runtime code. Of course, we can also opt for the worst of all possibilities: static const values of unnamed enumeration type: struct A { static const enum { v = 10 } value = v; }; This has every problem imaginable! ;) Personally, I use enumeration values for internal values and cast them to the appropriate type before any arithmetic or bit manipulation and use static constants for any external interface: template<class T> class X { private: enum { a = /* some calculation */, b = /* some calculation */ }; public: static const bool value = int(a) || int(b); }; template<class T> const bool X<T>::value; Paul Mensonides _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost