What's the difference between a function template argument and a function argument taking 'auto'?

Viewed 99

I don't see the difference between these two, could someone tell me if there are differences?

template <typename Arg1>
void templatedFuncAlsoTakingAutoArg(Arg1 arg1, auto arg2)
{
    // Both types are known at compile time for constexpr
    static constexpr decltype(arg1) variable1 = decltype(arg1)();
    static constexpr decltype(arg2) variable2 = decltype(arg2)();

    // And both types work with constexpr if so that invalid code
    // can appear within constexpr statements
    // If I pass in a double for arg1 or arg2 this still compiles
    if constexpr (std::is_integral_v<decltype(arg1)>)
    {
        arg1.non_existent_member = 7;
    }

    if constexpr (std::is_integral_v<decltype(arg1)>)
    {
        arg2.non_existent_member = 7;
    }
}

What I'm wondering is basically if it came down to writing a function what would be the difference if I wrote it like this:

template <typename Arg>
void likeThis(Arg arg){}

or:

void orLikeThis(auto arg) {}

In the template version you have the advantage of being able to refer to the type directly, but in the 'auto' version you can always get the type with decltype anyway, right? So what are the differences? Under the hood is the version that takes auto actually a template anyway, and is instantiated in the same way the compiler does templates?

2 Answers

but in the 'auto' version you can always get the type with decltype anyway, right?

Not necessarily. Conceptually doesn't change nothing, but the template/typename version is a little more flexible.

See the generic lambdas, introduced in C++14 using auto instead the template/typename syntax.

In C++20 are introduced the template lambda because you can't do something as follows

[]<std::size_t ... Is>(std::index_sequence<Is...>)
 { return (Is + ...); }
 (std::make_index_sequence<10u>{});

in a simple way, using auto.

EDIT: C++20 introduced the usage of auto as syntactic sugar for templates. The below would only apply to former C++ standards


It's important to remember, C++ is a statically typed language.

Using auto is like saying "figure out what type is suppose to go here and pretend like I wrote it out." This means that a variable with auto still will resolve to be a particular type. It does not mean that you can supply whatever type you want at runtime and expect it to figure it out, like you can with a dynamically typed language.

For that kind of functionality, you should use templates. But remember, you're still not supplying an arbitrary type at runtime and hoping that the code figures it out. At compile time, C++ needs to know what types you could use the function with, and then it will generate distinct versions of the function for those different types.

For example, if you used auto:

orLikeThis("one");
orLikeThis(1.0f);

...would fail to compile. The compiler has to assume that auto resolves to a single type, but since the code supplied both a string and a float, and now it can't resolve it to a single type.

However,

likeThis<string>("one")
likeThis<float>(1.1f)

Should compile. The compiler will create two different versions of the function to handle the different input types, but for convenience, you can simply provide a single function, and then just supply the type used.

So to answer your question of what the differences are, it comes down to how auto is syntactic sugar, and templates allow you to use multiple types in the "same" function.


TLDR; if you're looking to use multiple types, use a template; if you just want to avoid typing out the full type, then use auto. (Or if you're using C++20, auto will work for both use cases).

Related