/* Modern C++ templates */ #include #include #include #include #include /* available in C++ 2020 */ using namespace std; class bigobject {}; // the second argument to the template is statically determined template> class list : public enable_shared_from_this> { public: T car; shared_ptr> cdr; list(T a) { car=a; } list(T a, shared_ptr>&& b) // && means "R-value reference" { car = a; cdr = b;} // shared_from_this inherited from enable_shared_from_this shared_ptr> get_this() { return this->shared_from_this(); } int length() { int cx=0; auto current = get_this(); //shared_ptr>(this); for(;current;current=current->cdr) cx++; return cx; // return cx - car; // this will still compile! } // naturally polymorphic T largest() { T max = car; auto current = get_this(); for(;current;current=current->cdr) if (CMP{}(max,current->car)) max=current->car; return max ; } }; // constraining templates template struct addable // equivalent to interface { virtual T add(T other) = 0; }; struct rational : addable { int n; //numerator int d; // denominator rational(int a, int b) {n=a, d=b;} rational add(rational B) // returns structure by copy, not by pointer { rational sum(n*B.d+d*B.n, d*B.d); return sum; } }; // new C++ 2020 feature: a "concept" is a compile-time checked constraint: template concept canadd = std::is_base_of,T>::value; template requires canadd T add3(T a, T b, T c) { return a.add(b.add(c)); } // this is roughly equivalent to class> (java) // or class where T:IComparable (C#) template requires canadd T addvec(vector& v, T identity) { T sum = identity; for(auto x:v) sum = sum.add(x); return sum; } ///// the problem is that add3 will still compile without "requires..." // The only difference the concept appears to make is if I comment out // : addable in the definition of the struct, then it won't compile. // because of the "requires". However, if I also commented out the // "requires", it compiles again, because the template was instantiated // with a type that contains a .add function... I suppose this could be // useful in situations where "addable" is more than an interface and is // instead an "abstract class" with some pure virtual functions and some // implemented functions. int main() { auto n = make_shared>(3,make_shared>(5)); auto n2 = make_shared>(4,move(n)); // n is l-value, not r-value auto m = make_shared>(2.5); auto p = make_shared>("abc",make_shared>("123")); cout << "largest n2: " << n2->largest() << endl; cout << "length n2: " << n2->length() << endl; cout << "largest p: " << p->largest() << endl; cout << "length m: " << m->length() << endl; cout << "n use count: " << n.use_count() << endl; // note: n. not n-> //bigobject verybig(); //auto b = make_shared>>(big); //cout << b->largest(); // only now do you get an error, // when you're writing a template class, how are you sure it won't get // instantiated with a type that it can't handle? // C++ cannot type check template code beyond simple syntax errors vector V{rational(1,2), rational(1,4), rational(1,8)}; rational sum = addvec(V, rational(0,1)); cout << "sum: " << sum.n << "/" << sum.d << endl; return 0; }//main /* g++ -O2 -S templates.cpp std=c++20 No duplicate definitions for largest under level-2 optimization. Although much improved, modern C++ still must compile all the old code, some of which are very unsafe. There's no requirement that any of the new features must be used: in fact, the majority of C++ programmers today probably use a mix of modern and legacy features, which is not a recipe for safe programming. C++ in the past was too liberal in allowing new features to be added. For example, a C++ template can accept "higher-kinded types", which means that a template variable can itself be a template. But type theory is a mathematical concept and when you add something, there are formal properties that need to be verified - therorems to be proved. In the programming language research community, when a new language feature is proposed, you're expected to proof a set of theorems (among which type soundness is important). But C++ doesn't have such requirements and as a result, mathematically inconsistent concepts can find their way into C++, and these inconsistencies will soon or later lead to practical consequences. The academic research community generally has a low opinion of C++. But for a long time, we didn't have a viable alternative to offer to those who are doing low-level systems programming while still wanting the features of an advanced programming language. */ template typename B> // B is a higher-kinded type class C { B x; // B is itself a template };