On Wed, Oct 30, 2019 at 6:55 AM Richard Damon via Boost-users <boost-users@lists.boost.org> wrote:
On 10/30/19 1:03 AM, Zach Laine via Boost-users wrote:
>
> Yes, you got me.  I was speaking loosely, and referred to a template
> as if it were a type.  What I should have added was that a template's
> single responsibility should be to stamp out types that all model the
> same concept.  A policy-based template has a hard time doing that.  A
> policy-based template that stamps out strings with different
> invariants does not do that at all.
>
But all the various templates may well express a base concept even if
some of the invariants change between different template parameters. The
example of Unicode normalization, or memory allocator seem like perfect
examples of this. If an operation needs a particular normalization rule,
it specializes its parameter on that one case, otherwise it leaves it as
a template parameter.

To me, the basic invariant of a string is that it is a sequence of code
units that describe a textual object. Often the details of that encoding
are unimportant, it might be ASCII, it might be in some old code page,
it might be in UTF-8, it might be in UCS-4, and for the various Unicode
variations, there are different normalization rules to handle that a
given 'character' (aka Glyph) might be expressed in different ways, but
the routine largely doesn't care. When it does care, it can force the
string into that variant (or refuse some other variants), but the
purpose of templates is to condense duplicate code into a single piece
of code that only needs to be written once.

You're mixing kinds of abstractions here.  There is the genericity you find in a function that takes a generic parameter, and that's the kind of use based on concept you're talking about here.  About that you're 100% correct:

template<foo_concept T>
auto foo(T const & x); // <-- feel free to pass any type here that models foo_concept

Part of why the above code works is that foo() only uses x in certain ways, and anything that meets the syntactic requirements is well-formed.  If foo_concept describes a sequence container, I only care about the common interface of a sequence container.  Specifically, I cannot use vector::reserve(), and don't really care that it exists.

Where that breaks down is when you have not a function template that uses certain aspects of a type, but a class template that represents a set of types.  That case is different:

foo_template<T> foo; // <-- feel free to use the entire API

If the API is different for various values of T, such as it would be for a text template that instantiates as string-like or rope-like (because those have significantly different interfaces), that implies to me that I should have two names in play -- one for the string version and one for the rope version.  Otherwise, the result is super confusing for someone reading or writing code using the unified name.

Zach