David Abrahams wrote:
"Peter Dimov"
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
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? ;-)