
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Tobias Schwinger
It is not too clear whether it applies to partial template specialization, since we don't really "form" a type - we just consider a possibitity:
typedef int my_const_function() const;
template<typename T> struct remove_const { typedef T type; }; template<typename T> struct remove_const< T const > { typedef T type; };
// somwhere else remove_const<my_const_function>::type // ill-formed?
My main point in quoting the above is that "a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. i.e., it does not create a cv-qualified function type." Syntactically, saying 'const T' where 'T' is a function type is applying cv-qualification to the whole function type--which doesn't work. The cv-qualifiers on a function type (such as 'int () const') are only an internal part of the composite expression that forms a function type. Even syntactically, a cv-qualified function type (in the sense of 'const T' could not be written 'int () const' in the C++ syntax any more than 'int [] const' can be because of the wraparound declarator syntax.
There might be more references, but this gives a
significantly strong
implication that cv-qualification on function types is radically different than normal cv-qualification.
...and that cv-qualification on function types is intended for nonstatic member functions only (at least that's the impression I get from reading that paragraph).
The *types* cannot be used to declare anything else and no composite types (except pointers-to-members) can be formed from them (e.g. you can't have a regular pointer to a cv-qualified function type, such as 'int (*)() const').
The cv-qualifiers are modifying the implicit 'this' parameter. I.e. the cv-qualifiers don't modify the function type (all functions are 'const' because C++ doesn't treat functions as first-class objects); they modify an implicit formal parameter. So attempting to remove cv-qualification this way would be akin to transforming 'void (const int*)' into 'void (int*)'.
I don't find it very convincing -- we're talking about two different pairs of shoes here (syntax and type system vs. semantic treatment), IMO.
The syntax of the language, particularly the declarator syntax has no place for cv-qualification on wraparound declarators (like function types and array types). The trailing cv-qualifiers on a function type are a syntactic special case, are interpreted in a special way in the type system, and receive special semantic treatment. I.e. all parts of it are special cases that don't follow the general rule.
struct C { };
template<class T> struct is_const_function : is_pointer_to_const_member_function<T C::*> { };
Another thought on this one: as you said already, before forming a type C::*T we have to make sure it works for the T we have. But can we do it with T actually being a type which renders our program ill-formed when encountered (and without relying on undefined behaviour)?
You'd have to specialize-out any type that can't be the subject of a pointer-to-member. E.g. references are an example: template<class T> struct is_const_function<T&> { static const bool value = false; }; There aren't that many.
Yes. On the same token, however, I can just as easily write any other sort of function type manipulation that I might need. The point is that a library like yours should prevent the need for me to do it.
Right. But is there a need for raw, cv-qualified functions in the first place?
I don't know. What is the need for manipulating pointer-to-member-function types? The immediate difference is that pointers-to-members are class specific (i.e. you have to supply the C::* part where C is some type). What if you're passing them to something but want to apply the same function type to form multiple pointers to members of different classes? I.e. template<class T, class U, class F> class abc { typename return_type<F>::type func(F A::* p, F B::* q) { // ... } }; Note that I'm just pulling this out of the magical land of make-believe where I live. I don't directly have a need for this, but its something that someone might need for some reason.
However, I'm not concerned about preprocessing at all (I use preprocessed files). I also don't care about parsing time (it's comparatively little and it costs once per translation unit). What counts is the complexitiy of using the metafunctions.
Okay.
I don't find your suggestion of testing C::*T instead of T generally unattractive (and ideally it might simplify things). The conditions for applying it would be:
(when integrating) - be sure your suggestion builts on solid C++ (see above)
It is solid C++, it just isn't implemented correctly on several major C++ compilers.
- find a nice name to disambiguate const and volatile property tags so I won't need an underscore suffix
I used to dispise them, but I've found that you get used to them fairly quickly, and they just plain more convenient than any alternatives.
(when the library is used) - it works with the compiler - the configuration allows it by specifying the same calling conventions for member and non member functions (and there is no MSVC __thiscall weirdness around)
#if !MSVC ... all related functionality ... #endif
I have to annotate a tiny bit of rationale here: int func(int...) can be invoked like func(1), so it should be a valid argument for a generic facility that accepts a unary callable argument which in turn has an int parameter.
I agree, but it's likely to be extremely rare. The entire concept of passing (e.g.) a pointer to a variadic function to a generic facility is fraught with problems.
From what I have benchmarked so far it seems that adding (partial) specializations has a noticably smaller impact on the compilation time than adding instantiations (as required for decision making and matching these specializations twice).
What do you mean by, "as required for decision making and matching these specializations twice"? What decisions (during the execution of the code) are you talking about, and why would you need to match these specializations twice?
I'm all for completeness but I'm not convinced that what you are proposing is in fact about completeness and not about emulating consistency where there is none.
It's not about emulation, its about mirroring all of the related facilities that C++ has. Cv-unqualified function types are just a subset of function types as a whole. They are a refinement that introduces more capabilities.
Plus I'm wondering whether it's implementable in ISO C++ at all.
It is, but AFAIK they can only be matched/deduced with partial specializations. Regards, Paul Mensonides