C++ Logo

sg20

Advanced search

Re: [SG20] How much is too much with C++20 concepts?

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Tue, 3 Mar 2020 11:33:01 -0500
On Sun, Mar 1, 2020 at 1:11 AM Amir Kirsh via SG20 <sg20_at_[hidden]>
wrote:
>
> Substitute I mean not as a replacement for existing code. But rather: now
you can write instead that, in new code.
> So again the question is if (or when) the use of std::integral would be
actually advisable over let's say long long.
> Or what are the pros and cons.

The first (relevant) thing to teach students is the notion of a *function*.
    int triple(int x) { return 3 * x; }
The next thing is to teach that functions can be *overloaded* to form an
overload set.
    int triple(int x) { return 3 * x; }
    double triple(double x) { return 3 * x; }
    std::string triple(const std::string& x) { return x + x + x; }
The next thing is to teach that when you have a lot of overloads that look
similar, you want a cookie-cutter to stamp out similar overloads really
quickly. That's a *template* for making functions — a *function template*.
    template<class T> T triple(T x) { return 3 * x; }
Should you go straight to the C++20 syntax for "terse" function templates?
My opinion is a strong no, but I'm conservative in everything. ;) For the
sake of argument, let's say you present the C++20 syntax right away (and
deal with the inevitable confusion about the two different meanings of
`auto` in this snippet):
    auto triple(auto x) { return 3 * x; }
Now you are ready to show the problem with *mixing function overloads and
function templates* in the same overload set:
    auto triple(auto x) { return 3 * x; }
    std::string triple(const std::string& x) { return x + x + x; }
    int main() { triple("abc"); } // DOES NOT COMPILE
How do we fix this? Well, the old advice would have been, simply, for the
love of god don't mix functions and function templates in the same overload
set! Keep it simple! However, in C++20, maybe you'd be able to push the
complexity one step further: The problem with our "triple" overload set is
that triple<const char*>(const char*) is a better match for "abc" than
triple(const std::string&). It's always going to *be* better. The only way
to fix our overload set (other than completely rearchitecting it for
sanity, as in the C++17 course) is to indicate to the compiler that
triple<const char*>(const char*) *should never be allowed to exist*. We do
that by adding a *constraint* via a requires-clause.
    auto triple(auto x) requires std::arithmetic<(decltype(x))> { return 3
* x; }
    std::string triple(const std::string& x) { return x + x + x; }
    int main() { triple("abc"); } // OK!
Finally, C++20 allows us to use another terse syntax for the simplest
constraints. I strongly recommend against *ever* using this syntax in
production code, but for the sake of argument...
    auto triple(std::arithmetic auto x) { return 3 * x; }
    std::string triple(const std::string& x) { return x + x + x; }


When you ask:
> Then: what are the cases in which preserving the original type is a pro?
> Are there any performance advantages for one of the two in certain cases?
> Is one safer than the other in a way?

it's important to realize that you are asking about the pros and cons
of *function
templates versus non-template functions*. The pros and cons have not
changed since 1984.

- Templates are polymorphic rather than monomorphic.
- Template instantiations are individually codegenned and thus can give
highly specialized and optimized code.
- Template instantiations are individually codegenned and thus can produce
"template bloat."
- Templates can be harder to reason about, because it's like debugging a
function where every variable is defined `auto`. (We saw that in the
`triple("abc")` example.)
- Templates can be harder to debug in a debugger, because it's like
debugging an inline function. Lines of source don't correspond to lines of
assembly.
- Templates must be defined in header files. (Or, in C++20, they can be
defined in modules. Modules have their own set of pros and cons.)
In brief, templates produce *highly specialized codegen for polymorphic
code* but *pay a cost in being relatively harder to use and maintain*.
C++20 doesn't change anything here.

–Arthur

Received on 2020-03-03 10:38:37