$include_dir="/home/hyper-archives/boost-users/include"; include("$include_dir/msg-header.inc") ?>
From: Peter Dimov (pdimov_at_[hidden])
Date: 2005-03-05 17:42:13
David Abrahams wrote:
> "Peter Dimov" <pdimov_at_[hidden]> writes:
>
>> The placeholders do have the same meaning; they are an example of a
>> de-facto standard.
>> The conflicts that exist are (a) resolvable, (b) caused by factors
>> that do not apply to our discussion.
>
> Not exactly.  I'm thinking of MPL and bind actually.  They don't have
> the same meaning, only a similar one, and they can't be unified.  It's
> the very "standard-ness" that prompted MPL to use the same names in a
> very similar -- but not identical -- way.
Well, the problem here is that (a non-macro) _1 can't be a value and a type 
at the same time, not that the two _1s express different notions.
>> And for the record, Lambda's placeholders cause exactly the same ODR
>> violations, although I'm not sure whether "cause" is the proper term.
>
> You're right, "lead to" would be more accurate.  I hope you understand
> the following:
>
> 1. I'm arguing about this because I want to end up convinced of
>   something, and soon -- I'll need to choose customization techniques
>   imminently -- so thank you for engaging me on this.
>
> 2. I only brought up the ODR thing as a piece of evidence that
>   problems don't neccessarily get resolved quickly
Yes, I understood (2). With regard to (1), I'm not sure that I will convince 
you of something. My own position is that one should pick overloading or 
specializations based on whether exact type matching better fits... no, make 
that "is essential for" the design. A secondary concern that sometimes 
outweighs the exact type matching advantage is the syntactic penalty 
introduced by specialization-based customization points because (a) 
functions can't be partially specialized, (b) specializations need to be 
defined in their namespace.
Potential identifier conflicts come a distant third to me. I understand the 
problem and the implications, I just don't give it that much weight.
>>> That's why generic library writers have developed a guideline to
>>> write concept requirements that don't depend on member functions.
>>
>> Maybe they did. My point is that I never see anti-v.begin()
>> campaigns;
>
> I do.  Why do you think Boost.Range is using free functions?  Not
> putting member functions in concept requirements is a well-known
> generic programming guideline.  For years even Scott Meyers has been
> saying that "interfaces should be extended with free functions," which
> isn't viewed through the generic customization-point lens, but means
> the same thing.
Scott Meyers's point is that operations that can be expressed in terms of 
the public interface should be non-friends, which is not the same thing at 
all.
The primary problem with member functions (and types) as a concept 
requirement is that they can't be retrofitted to a type. The fact that a 
type can't have two members with the same name is a secondary concern, which 
goes relatively unnoticed compared to the "ADL problem".
>> and the debate is whether not having to
>> worry is a good thing. Customization points are very important and
>> need to be treated with caution.
>
> What kind of caution, if they are associated with a namespace, and
> why?  We could use std::iterator_traits as an example.
Once these customization points become well-known, more and more types start 
to conform to them, and more and more other libraries start to depend on the 
customization point. This effectively means that this particular 
customization point can no longer be changed by the original author, because 
this will break too much code. In some cases it even changes the meaning of 
the customization point (usually to a subset of the original).
>> Once they become de-facto standards, the library no
>> longer owns them, even if they are in the library's own namespace.
>
> Even if I understood what you meant by "the library no longer owns
> them, even if they are in the library's own namespace," what are the
> implications of that?  Is std::iterator_traits an example of a
> de-facto standard customization point in a namespace?
The implication of that is that customization points need to be minimized 
and their semantics - carefully chosen. They are even more important than 
the (rest of the) public interface of the library, because they can affect 
and shape the code of people that don't even use this particular library.
I see potential identifier collisions as just one of the things can go 
wrong, not as the only danger.
To take iterator_traits<X>::reference as an example, a carefully chosen 
meaning for it would probably be "the return type of the expression *x", 
which isn't really domain specific. Once libraries start using it you can no 
longer turn back and redefine it as something that only makes sense for 
iterators. difference_type, the return type of the expression x - y, also 
makes sense in a non-iterator context.
> There are clearly some (just a very few, IMO) customization points
> like swap that are really intrinsic.  They have to do with the
> semantics of what Stepanov and friends are calling "value types."  It
> seems to me as though the rest are associated with a particular
> domain, and it's appropriate to name that domain.
I can agree that most customization points are associated with a particular 
domain, but this doesn't mean that they are associated with a particular 
library from that domain. In my experience - which is not that extensive - 
well thought out customization points can be, and are, used outside of the 
context of the library that created them. They are a part of a domain to the 
extent that the type they are associated with is part of that domain, not 
because of the originating library.
Example: I can use intrusive_ptr_add_ref to increment the reference count of 
a type without ever including <boost/intrusive_ptr.hpp>. It is a way for a 
type to advertize itself as intrusively counted in general, not as suitable 
for use with boost::intrusive_ptr in particular. If every intrusively 
counted smart pointer has a different customization interface, users are 
protected from collisions, but need to mark their types as intrusively 
counted multiple times, to satisfy each library. They would certainly prefer 
the smart pointer authors getting together, so to speak, and settling on one 
interface.
But you'll note that the function is not named add_ref, after all. ;-)
>> OK; so what alternatives do we have?
>>
>> 1. lib1::numeric_traits<X>::zero( x ); // somewhat unwieldy
>> 2. lib1::zero( x ); // syntactic sugar for the above
>> 3. lib1_zero( x );
>>
>> What, exactly, are the advantages of #2 over #3?
>
> #3 invokes ADL.
Yes, yes, but what are the advantages of #2 over #3? ;-)