The concepts proposals that have come out of Indiana have had the notion of “concept friends” and “concept map friends”, which allow classes to declare concepts or concept maps as friends. Although the feature has never been implemented in ConceptGCC, it’s intent and use were obvious: if you wanted to write a concept map for a particular concept, but that concept map required access to some of the private members of the class, one would use a concept map friend. It’s easy to construct an example where this seems plausible:
concept Vector<typename T> {
typename scalar;
int length(const T&);
scalar& operator[](T&, int);
// ...
};
// A class for complex numbers
template<typename T>
class complex {
public:
// ...
T real() const;
void set_real(T x);
T imag() const;
void set_imag(T x);
private:
T real_;
T imag_;
friend concept_map Vector<complex<T> >;
};
// Make an std::complex into a Vector
template<typename T>
concept_map Vector<complex<T> > {
typedef T scalar;
int length(const complex<T>&) { return 2; }
T& operator[](complex<T>& c, int i) {
return i == 0? c.real_ : c.imag_;
}
};
In this example, the author of complex<T> chose to provide accessors to the real and imaginary parts of the complex number, but there is no way in the public interface to get a true reference to the underlying variables. However, the Vector concept requires access to the values through an actual reference. Concept map friends save the day, by allowing the concept map to access the private members of complex, thereby exposing the references.
Was this the right solution? I don’t know. One could make the case that this complex template really doesn’t meet the requirements of the Vector concept, because the author chose to hide the implementation details for a reason (e.g., they may change). Introducing the concept friend is really a hack that exposes more implementation details of complex, but without making those implementations part of the public interface. Granted, this is a general argument against friends, which do have their uses. What about this particular use?
Another way to consider whether concept map friends were the right solution here is to think of the multi-author, multi-library case. It’s likely that Vector was written for a particular library, then complex was written for a different library by a different programmer. Now, a third person, integrating those two libraries, needs to make a complex model Vector. Unfortunately, this third person can’t make this happen without modifying the definition of complex to introduce a new concept friend declaration. In other words, concept friends do not permit truly retroactive modeling. To do that, we would need to be able to declare friendly outside of the class definition, but such a feature completely breaks encapsulation. Thus, there are at least four ways we could go with concept (map) friends:
- Eliminate them entirely, because we don’t believe that they are really necessary or are not sufficiently useful to warrant specifying and implementing them.
- Allow them only in class definitions, so that we can break encapsulation in small ways when we need to, just like we can with classes and templates today.
- Allow them anywhere, so that we could break encapsulation when needed. However, this means that encapsulation wouldn’t really exist at all, since it would be so easy to break.
- Eliminate access checking within concept maps, allowing any concept map to access the private members of any class. This makes concept maps somehow “special”, because they have access to everything, but solves the problem with a minimum specification burden.