How will Concepts-Lite interact with variadic templates? - c++

I watched Bjarne Strustrup's talk in Going Native 2013 and he gives the following example for the upcoming concepts-lite feature of C++.
void sort(Container& c); // terse notation
// Expands to
template <Container __Cont>
void sort(__Cont& c); // shorthand notation
// Expands to
template <typename __Cont>
requires Container<__Cont>()
void sort(__Cont & c);
My question is how will this work with variadic templates?
Say I want to define a variadic maximum function using a Comparable concept. Will the following syntax be accepted?
auto maximum(Comparable a)
{
return a;
}
auto maximum(Comparable c, Comparable... rest)
{
return std::max(a, maximum(rest...));
}
If so will Comparable... mean that all the elements in the parameter pack are the same type or just that they are all Comparable types so that the pack may include both int and string? (which are both comparable but not to each other)
Curious minds want to know.

I believe the intention of the terse format is to require all elements in the pack must be of the same type. This is derived from the example in n3701 §5.3, which states that
void sort(Random_access_iterator p, Random_access_iterator q);
should be equivalent to
template<Random_access_iterator __Ran>
void sort(__Ran p, __Ran q);
because
"By default, if you use the same constrained parameter
type name for two arguments, the types of those arguments must be the same.
We chose to make repeated use of a constrained parameter type name imply
“same type” because that (in most environments) is the most common case,
it would be odd to have an identifier used twice in a scope have two different
meanings, and the aim here is to optimize for terse notation of the simplest case."
But I don't see how it can be expanded into your maximum with the current ('14) template syntax. In proposed change of standard wording in n3701 it only talked about the simple case
§7.1.6.5 Constrained type specifiers [dcl.spec.constrained]
...
The first use of concept-name or partial-concept-id within a scope binds that
name to the placeholder type so that subsequent uses of the same name refer to
the same type.
But it doesn't explain the interaction of variadics. Maybe need to wait for the 3rd revision to clarify this.

I don't know if N3580 is the latest version of the concept paper but in 4.5.1 it seems to describe how type requirements can be used with variadic argument lists. It seems that
template <Comparable... T>
auto maximum(T... values) -> <return-type-goes-here>
would require that each of the arguments satisfies the Comparable requirement but there is no restriction that the types T... are all the same. This requirement would need to be imposed separately. Off-hand, the only way I'd know how to impose the same type requirement is the same approach as in C++11 but the concepts facilities may have a better approach.

Related

Why does accumulate in C++ have two templates defined

Why does accumulate in C++ have two templates defined when the job can be done with just one template (the one with the binaryOperation and default value to sum)?
I am referring to the accumulate declaration from http://www.cplusplus.com/reference/numeric/accumulate/
Because that's how the standard has been specified.
It is often a matter of taste whether to use an overload or a default argument. In this case, overload was chosen (by committee, by Alexander Stepanov, or by whoever happened to be responsible for the choice).
Default values are more limited than overloads. For example, you can have a function pointer T (*)(InputIterator, InputIterator, T) pointing to the first overload, which would not be possible if there was only one function (template) with 4 arguments. This flexibility can be used as an argument for using overloads rather than default arguments when possible.
It's true you would get mostly the same behavior from a single template like
template <class InputIt, class T, class BinaryOperation = std::plus<>>
accumulate(InputIt first, InputIt last, T init, BinaryOperation op = {});
But note that in earlier versions of C++, this would be difficult or impossible:
Prior to C++11, a function template could not have default template arguments.
Prior to C++14, std::plus<> (which is the same as std::plus<void>) was not valid: the class template could only be instantiated with one specific argument type.
The accumulate template is even older than the first C++ Standard of 1998: it goes back to the SGI STL library. At that time, compiler support for templates was rather inconsistent, so it was advisable to keep templates as simple as possible.
So the original two declarations were kept. As noted in bobah's answer, combining them into one declaration could break existing code, since for example code might be using a function pointer to an instantiation of the three-argument version (and function pointers cannot represent a default function argument, whether the function is from a template or not).
Sometimes the Standard library will add additional overloads to an existing function, but usually only for a specific purpose that would improve the interface, and when possible without breaking old code. There hasn't been any such reason for std::accumulate.
(But note member functions in the standard library can change more often than non-member functions like std::accumulate. The Standard gives implementations permission to declare member functions with different overloads, default arguments, etc. than specified as long as the effects are as described. This means it's generally a bad idea to take pointers to member functions to standard library class members, or otherwise assume very specific declarations, in the first place.)
The motivtion for the 2 functions is the same reason that we have both a copy and a transform function, to give the coder the flexability to apply a function on a per element basis. But perhaps some real world code would be helpful in understanding where this would be used. I've used both these snipits professionally in coding:
The 1st instance of accumulate can be used to sum the elements of a range. For example, given const int input[] = { 13, 42 } I can do this to get the sum of all elements in input:
accumulate(cbegin(input), cend(input), 0) /* Returns 55 */
I personally most commonly use the 2nd instance to generate strings (because it's the closest thing c++ has to a join) but it can also be used when special preprocessing is needed before the element is added. For example:
accumulate(next(cbegin(input)), cend(input), to_string(front(input)), [](const auto& current_sum, const auto i){ return current_sum + ", " + to_string(i); }) /* Returns "13, 42"s */
It's worth noting P0616R0 when considering my use of the 2nd function. This proposal has been accepted into c++20 and will move rather than copy the first parameter to accumulate's functor, which, "Can lead to massive improvements (particularly, it
means accumulating strings is linear rather than quadratic)."

Why does std::get not have a single signature that accepts a forwarding reference

Why does std::get for std::tuple have so many overloads (http://en.cppreference.com/w/cpp/utility/tuple/get)? One corresponding to every possible combination of const & and &&? With each combination the const ref qualifiers on the return value is the same. Why not have just one single overload that accepts the tuple type by forwarding reference and then simply forward the return value based on the signature of the input? Something like so
template <int Index, typename TupleType>
decltype(auto) get(TupleType&& tup);
This sort of thing would make it really easy for people to reason about what the get function does and would avoid bugs like Issue 2485 (https://wg21.cmeerw.net/lwg/issue2485)
std::get existed prior to decltype(auto). So that was easy.
Ok, why not change it?
The body of std::get is not specified by the standard, and should not be as different compilers have different layouts and implementations of tuples.
decltype(auto) does not tell the reader of the standard, the user or implementor of a compiler, what the return type is. It just says it is deduced from the body. Which the standard does not specify.
It being in the standard like that would thus be useless, unless they described what the return value was separately, which in the end would look a lot like listing overloads.
The parameter of std::get cannot be deduced, because std::get is defined for many different types. (Ex. tuple, pair, array, variant).
For this reason more than any other std::get cannot deduce its parameter type. Changing it to deduction at this point would break real-world code, such as types deduced from tuple. For example the suggested signature change would yield a signature like:
template <class TupleLike, class = enable_if_t<IsStdTuple<TupleLike>::value>>>
decltype(auto) get(TupleLike&&);
This signature does not allow implicit conversions to the tuple type in the interface, like the older signatures did. As mentioned this breaks types which derive from std::tuple or types that have a conversion operator to std::tuple.
Libc++ implements the tuple, pair and array converting constructors using a single generic TupleLike overload. As the maintainer I'm intimately familiar with these implementation issues, and if I could implement a conforming std::get as you propose I already would.

Are C++ concepts a form of existential type?

I was looking at the definition of existential types on Wikipedia (Existential_types) and it feels similar in some way to concepts in C++ (particularly to concepts lite).
Are C++ concepts a form of existential type?
If not, what are the differences between the two?
TL;DR: Yes, Concepts are (or at least allow you to define) existential types.
Here's my reasoning, though be warned; I'm not a type theorist:
Consider Wikipedia's definition of abstract data type (emphasis mine):
In computer science, an abstract data type (ADT) is a mathematical model for a certain class of data types of one or more programming languages that have similar semantics. An abstract data type is defined indirectly, only by the operations that may be performed on it and by mathematical constraints on the effects (and possibly cost) of those operations.
Existential types, as described by these two Stack Overflow questions and the Wikipedia article you linked, seem to be a way of modelling abstract data types using parameterized definitions. Importantly, those parameters are not part of the resulting existential type.
At face value, a concept on the other hand is a predicate on one (zero?) or more types, which can be used to restrict templates. It's not obvious that they bear any relation to existential types— until you consider requires clauses.
Basically, requires allows you to test for certain properties of types. Among these are whether they define a certain member type, have a certain member function, are convertible to certain types, etc. This observation (the main point of design, really) is where the meat of the matter lies.
It seems to me, at least, that what concepts fundamentally are is a mechanism for defining abstract data types. This is where we begin to see the similarity to existential types: they model ADTs by parameterization, and more importantly, allow you to define the ADT without exposing the parameters.
Take the Container concept, for example. You may, with Concepts Lite, write something like
void print (Container c) {
for (const auto& e : c)
print (e);
}
// Later
print (std::vector <int> {1, 2, 3, 4});
This works because there exists some type I such that the expressions begin (c) and end (c) return objects of type I, along with Container's other constraints. That's existential quantification; Container is an existential type.
As far as I know, C++ concepts are arbitrary type predicates. The work on C++ concepts concentrates more on how these predicates integrate into the language rather than on giving a particular meaning or specifying a mathematical / logical model. The idea is that exactly as a function
void f(double x);
is clearly expecting a parameter of type double, in such a simple way
template <Container C>
void f(const C& c);
is expecting not just a typename but a Container. Now, how is Container defined? It could be e.g.
template <typename T>
struct Container: std::false_type { };
template <typename T, size_t N>
struct Container <std::array<T, N> >: std::true_type { };
template <typename T, typename A>
struct Container <std::vector<T, A> >: std::true_type { };
and so on. Predicates like Container exist now, but to integrate them into a template function requires inconvenient constructs like std::enable_if. Concepts will make this cleaner and easier to use.
This again, is just roughly my understanding.

What is the equivalent of cast for concepts?

Consider a class A satisfies two concepts ConceptA and ConceptB. Let a function foo is overloaded for the two concepts:
void foo(ConceptA& arg);
void foo(ConceptB& arg);
A a;
fun(concept_cast<ConceptA>(a));
Note: This example uses the "Terse Notation" syntax proposed as part of N3701, §5
Is there something exist like concept_cast which allows users to select the overload?
Eg:
Lets say
ConceptA says T has to have a member function bar()
ConceptB says T has to have a member function baz()
and class A has both bar() and baz() member function
Its clearly ambiguous, but is there a way to explicitly select like we have static_cast for normal overloads?
Update: Accepted answer is more than 2 years old. Any update in c++17?
If one of the concepts is a more constrained version of the other, (e.g. everything that satisfies ConceptA will also satisfy ConceptB but not vice versa), then the most-constrained overload that A satisfies will be chosen.
If neither concept is more constrained than the other, then the two are considered to be ambiguous overloads. Given how you phrased the question, I expect you already knew this.
Regarding concept_cast, I don't think there's anything like that in the current proposal. At least not as of the Bristol meeting (Apr '13). I don't expect this to have changed as the current focus seems to be on making sure the core of the concepts-lite/constraints proposal is workable and acceptable to the committee.
There'll probably be some demand for explicitly picking overloaded template functions like that, and maybe such a cast is the right thing, but I'm not so sure. Consider that such a cast would only be useful for overload disambiguation, where as static_cast is a more general feature. The result of the concept_cast would be same as the original value outside the context of overload resolution!
Edit: Looking at the latest proposal (N3701), there's no provision for explicitly specifying which template function to instantiate.
Your claim that static_cast can be used to explicitly select a 'normal' overload is specious. It is possible to write the following in today's C++:
template<typename P, EnableIf<NullablePointer<P>>...>
void foo(P&);
template<typename It, EnableIf<Iterator<It>>...>
void foo(It&);
Assuming that NullablePointer and Iterator perform concept checking for the associated Standard concepts, then int* q; foo(q); has no hope of compiling because int* is both a model of NullablePointer and of Iterator (and neither concept subsumes the other). There's nothing obvious to static_cast to to help with that situation.
My example (that you can test for yourself) is extremely relevant because this kind of code is what Concepts Lite attempts to formalize. The overload set you're presenting is equivalent to:
template<typename A>
requires ConceptA<A>
void foo(A& arg);
template<typename B>
requires ConceptB<B>
void foo(B& arg);
Notice the similarity between the requires clauses and the EnableIf 'clauses'.

Does C++11 support types recursion in templates?

I want to explain the question in detail. In many languages with strong type systems (like Felix, Ocaml, Haskell) you can define a polymorphic list by composing type constructors. Here's the Felix definition:
typedef list[T] = 1 + T * list[T];
typedef list[T] = (1 + T * self) as self;
In Ocaml:
type 'a list = Empty | Cons ('a, 'a list)
In C, this is recursive but neither polymorphic nor compositional:
struct int_list { int elt; struct int_list *next; };
In C++ it would be done like this, if C++ supported type recursion:
struct unit {};
template<typename T>
using list<T> = variant< unit, tuple<T, list<T>> >;
given a suitable definition for tuple (aka pair) and variant (but not the broken one used in Boost). Alternatively:
using list<T> = variant< unit, tuple<T, &list<T>> >;
might be acceptable given a slightly different definition of variant. It was not possible to even write this in C++ < C++11 because without template typedefs, there's no way to get polymorphism, and without a sane syntax for typedefs, there's no way to get the target type in scope. The using syntax above solves both these problems, however this does not imply recursion is permitted.
In particular please note that allowing recursion has a major impact on the ABI, i.e. on name mangling (it can't be done unless the name mangling scheme allows for representation of fixpoints).
My question: is required to work in C++11?
[Assuming the expansion doesn't result in an infinitely large struct]
Edit: just to be clear, the requirement is for general structural typing. Templates provide precisely that, for example
pair<int, double>
pair<int, pair <long, double> >
are anonymously (structurally) typed, and pair is clearly polymorphic. However recursion in C++ < C++11 cannot be stated, not even with a pointer. In C++11 you can state the recursion, albeit with a template typedef (with the new using syntax the expression on the LHS of the = sign is in scope on the RHS).
Structural (anonymous) typing with polymorphism and recursion are minimal requirements for a type system.
Any modern type system must support polynomial type functors or the type system is too clumbsy to do any kind of high level programming. The combinators required for this are usually stated by type theoreticians like:
1 | * | + | fix
where 1 is the unit type, * is tuple formation, + is variant formation, and fix is recursion. The idea is simply that:
if t is a type and u is a type then t + u and t * u are also types
In C++, struct unit{} is 1, tuple is *, variant is + and fixpoints might be obtained with the using = syntax. It's not quite anonymous typing because the fixpoint would require a template typedef.
Edit: Just an example of polymorphic type constructor in C:
T* // pointer formation
T (*)(U) // one argument function type
T[2] // array
Unfortunately in C, function values aren't compositional, and pointer formation is subject to lvalue constraint, and the syntactic rules for type composition are not themselves compositional, but here we can say:
if T is a type T* is a type
if T and U are types, T (*)(U) is a type
if T is a type T[2] is a type
so these type constuctors (combinators) can be applied recursively to get new types without having to create a new intermediate type. In C++ we can easily fix the syntactic problem:
template<typename T> using ptr<T> = T*;
template<typename T, typename U> using fun<T,U> = T (*)(U);
template<typename T> using arr2<T> = T[2];
so now you can write:
arr2<fun<double, ptr<int>>>
and the syntax is compositional, as well as the typing.
No, that is not possible. Even indirect recursion through alias templates is forbidden.
C++11, 4.5.7/3:
The type-id in an alias template declaration shall not refer to the alias template being declared. The type produced by an alias template specialization shall not directly or indirectly make use of that specialization. [ Example:
template <class T> struct A;
template <class T> using B = typename A<T>::U;
template <class T> struct A {
typedef B<T> U;
};
B<short> b; // error: instantiation of B<short> uses own type via A<short>::U
— end example ]
If you want this, stick to your Felix, Ocaml, or Haskell. You will easily realize that very few (none?) sucessful languages have type systems as rich as those three. And in my opinion, if all languages were the same, learning new ones wouldn't be worth it.
template<typename T>
using list<T> = variant< unit, tuple<T, list<T>> >;
In C++ doesn't work because an alias template doesn't define a new type. It's purely an alias, a synonym, and it is equivalent to its substitution. This is a feature, btw.
That alias template is equivalent to the following piece of Haskell:
type List a = Either () (a, List a)
GHCi rejects this because "[cycles] in type synonym declarations" are not allowed. I'm not sure if this is outright banned in C++, or if it is allowed but causes infinite recursion when substituted. Either way, it doesn't work.
The way to define new types in C++ is with the struct, class, union, and enum keywords. If you want something like the following Haskell (I insist on Haskell examples, because I don't know the other two languages), then you need to use those keywords.
newtype List a = List (Either () (a, List a))
I think you may need to review your type theory, as several of your assertions are incorrect.
Let's address your main question (and backhanded point) - as others have pointed out type recursion of the type you requested is not allowed. This does not mean that c++ does not support type recursion. It supports it perfectly well. The type recursion you requested is type name recursion, which is a syntactic flair that actually has no consequence on the actual type system.
C++ allows tuple membership recursion by proxy. For instance, c++ allows
class A
{
A * oneOfMe_;
};
That is type recursion that has real consequences. (And obviously no language can do this without internal proxy representation because size is infinitely recursive otherwise).
Also C++ allows translationtime polymorphism, which allow for the creation of objects that act like any type you may create using name recursion. The name recursion is only used to unload types to members or provide translationtime behavior assignments in the type system. Type tags, type traits, etc. are well known c++ idioms for this.
To prove that type name recursion does not add functionality to a type system, it only needs to be pointed out that c++'s type system allows a fully Turing Complete type calculation, using metaprogramming on compiletime constants (and typelists of them), through simple mapping of names to constants. This means there is a function MakeItC++:YourIdeaOfPrettyName->TypeParametrisedByTypelistOfInts that makes any Turing computible typesystem you want.
As you know, being a student of type theory, variants are dual to tuple products. In the type category, any property of variants has a dual property of tuple products with arrows reversed. If you work consistently with the duality, you do not get properties with "new capabilities" (in terms of type calculations). So on the level of type calculations, you obviously don't need variants. (This should also be obvious from the Turing Completeness.)
However, in terms of runtime behavior in an imperative language, you do get different behavior. And it is bad behavior. Whereas products restrict semantics, variants relax semantics. You should never want this, as it provably destroys code correctness. The history of statically typed programming languages has been moving towards greater and greater expression of the semantics in the type system, with the goal that the compiler should be able to understand when the program does not mean what you want it to. The goal has been to turn the compiler into the program verification system.
For instance, with type units, you can express that a particular value isn't just an int but is actually an acceleration measured in meters per square seconds. Assigning a value that is a velocity expressed in feet per hour divided by a timespan of minutes shouldn't just divide the two values - it should note that a conversion is necessary (and either perform it or fail compilation or... do the right thing). Assinging a force should fail compilation. Doing these kinds of checks on program meaning could have given us potentially more martian exploration, for instance.
Variants are the opposite direction. Sure, "if you code correctly, they work correctly", but that's not the point with code verification. They provably add code loci where a different engineer, unfamiliar with current type usage, can introduce the incorrect semantic assumption without translation failure. And, there is always a code transformation that changes an imperative code section from one that uses Variants unsafely to one that use semantically validated non-variant types, so their use is also "always suboptimal".
The majority of runtime uses for variants are typically those that are better encapsulated in runtime polymorphism. Runtime polymorphism has a statically verified semantics that may have associated runtime invariant checking and unlike variants (where the sum type is universally declared in one code locus) actually supports the Open-Closed principle. By needing to declare a variant in one location, you must change that location everytime you add a new functional type to the sum. This means that code never closes to change, and therefore may have bugs introduced. Runtime polymorphism, though, allows new behaviors to be added in separate code loci from the other behaviors.
(And besides, most real language type systems are not distributive anyway. (a, b | c) =/= (a, b) | (a, c) so what is the point here?)
I would be careful making blanket statements about what makes a type system good without getting some experience in the field, particularly if your point is to be provocative and political and enact change. I do not see anything in your post that actually points to healthy changes for any computer language. I do not see features, safety, or any of the other actual real-world concerns being addressed. I totally get the love of type theory. I think every computer scientist should know Cateogry Theory and the denotational semantics of programming languages (domain theory, cartesian categories, all the good stuff). I think if more people understood the Curry-Howard isomorphism as an ontological manifesto, constructivist logics would get more respect.
But none of that provides reasons to attack the c++ type system. There are legitimate attacks for nearly every language - type name recursion and variant availability are not them.
EDIT: Since my point about Turing completeness does not seem to be understood, nor my comment about the c++ way of using type tags and traits to offload type calculations, maybe an example is in order.
Now the OP claims to want this in a usage case for lists, which my earlier point on the layout easily handles. Better, just use std::list. But from other comments and elsewhere, I think they really want this to work on the Felix->C++ translation.
So, what I think the OP thinks they want is something like
template <typename Type>
class SomeClass
{
// ...
};
and then be able to build a type
SomeClass< /*insert the SomeClass<...> type created here*/ >
I've mentioned this is just a naming convention wanted. Nobody wants typenames - they are transients of the translation process. What is actually wanted is what you will do with Type later on in the structural composition of the type. It will be used in typename calculations to produce member data and method signatures.
So, what can be done in c++ is
struct SelfTag {};
Then, when you want to refer to self, just put this type tag there.
When it's meaningful to do the type calculation, you have a template specialisation on SelfTag that will substitute SomeClass<SelfTag> instead of substituting SelfTag in the appropriate place of the type calculation.
My point here is that the c++ type system is Turing Complete - and that means a lot more than what I think the OP is reading everytime I've written that. Any type calculation may be done (given constraints of compiler recursion) and that really does mean that if you have a problem in one type system in a completely different language, you can find a translation here. I hope this makes things even clearer about my point. Coming back and saying "well you still can't do XYZ in the type system" would be clearly missing the point.
C++ does have the "curiously recurring template pattern", or CRTP. It's not specific to C++11, however. It means you can do the following (shamelessly copied from Wikipedia):
template <typename T>
struct base
{
// ...
};
struct derived : base<derived>
{
// ...
};
#jpalcek answered my question. However, my actual problem (as hinted at in the examples) can be solved without recursive aliases like this:
// core combinators
struct unit;
struct point;
template<class T,class U> struct fix;
template<class T, class U> struct tup2;
template<class T, class U> struct var2;
template <> struct
fix<
point,
var2<unit, tup2<int,point> >
>
{
// definition goes here
};
using the fix and point types to represent recursion. I happen not to require any of the templates to be defined, I only need to define the specialisations. What I needed was a name that would be the same in two distinct translation units for external linkage: the name had to be a function of the type structure.
#Ex0du5 prompted thinking about this. The actual solution is also related to a correspondence from Gabriel des Rois many years ago. I want to thank everyone that contributed.