+100

Arthur, you are my hero! But....

What I explain in addition, is that the case with

    template<class T> T triple(T x) { return 3 * x; }

restricts the template parameter based on its usage, that I would spell 'typename T' requires to be copyable and multiplyable with an int. This is the actual concept of the template parameter (I am teaching the concept of a template parameter like this at least for 10 years, without the special syntax we get). Violating the concept, as with a string, will result in a compile error. SFINAE and overloads allow to special case the template, that is where C++20 concepts syntax kicks in and make things easier to spell than typical std::enable_if...

I am not that much against mixing templates and non-templates in an overload set, e.g., for special casing, what you must not to is trying to specialize function templates, which get only applied, when the generic template is selected by overload resolution, use overloads instead.

Regards
Peter.

Arthur O'Dwyer via SG20 wrote on 03.03.20 17:33:
On Sun, Mar 1, 2020 at 1:11 AM Amir Kirsh via SG20 <sg20@lists.isocpp.org> 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



--
Peter Sommerlad

Better Software: Consulting, Training, Reviews
Modern, Safe & Agile C++

peter.cpp@sommerlad.ch
+41 79 432 23 32