Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::pair<auto, auto> return type

I was playing around with auto in std::pair. In the below code, function f is supposed to return a std::pair of types which depend on a template parameter.

A working example:

EXAMPLE 1

template <unsigned S>
auto f()
{
    if constexpr (S == 1)
        return std::pair{1, 2}; // pair of ints
    else if constexpr (S == 2)
        return std::pair{1.0, 2.0}; // pair of doubles
    else
        return std::pair{0.0f, 0.0f}; // pair of floats
}

This works with gcc 9.2, gcc 10.0, clang 9.0 and clang 10.0.

Next, I wanted to explicitly write the return type as a std::pair for clarity reasons:

EXAMPLE 2

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return {1, 2};
    /* ... */
}

Both gcc 9.2/10.0 and clang 9.0/10.0 failed to compile this.

gcc 9.2

error: invalid use of 'auto'
error: template argument 1 is invalid // first argument (auto) of std::pair
error: template argument 2 is invalid // second argument (auto) of std::pair
error: cannot convert '<brace-enclosed initializer list>' to 'int' in return

From the last error message, gcc 9.2 seems to believe that std::pair<auto, auto> is an int. How can this be explained?

gcc 10.0

error: returning initializer list

This error is understandable, however, I expected the constructor of std::pair to be invoked, or is there something I am missing here?

clang 9.0 and 10.0

'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'

Ok, clang doesn't like any of this. From the second error message, it seems that clang also believes the return type is int.

Finally, to fix the error obtained compiling with gcc 10.0, I decided to return a std::pair explicitly:

EXAMPLE 3

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return std::pair{1, 2};
    /* ... */
}

clang 9.0 and 10.0

Same as before, but with an additional:

no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'

Here clang still thinks we are returning an int?

gcc 9.2

Same as before.

gcc 10.0

It works!

I guess some features still have to be implemented, or in one of the situations described above, is there a compiler which is right and the other wrong? In my opinion, example 2 should work. Or should it not?

like image 719
mfnx Avatar asked Jan 03 '20 12:01

mfnx


People also ask

Can auto be a return type in C++?

In C++14, you can just use auto as a return type.

Which C++ added feature of Auto for return type of function?

C++: “auto” return type deduction The “auto” keyword used to say the compiler: “The return type of this function is declared at the end”. In C++14, the compiler deduces the return type of the methods that have “auto” as return type.

What does Decltype auto do?

decltype(auto) is primarily useful for deducing the return type of forwarding functions and similar wrappers, where you want the type to exactly “track” some expression you're invoking.

Which is the default return type specifier in C Plus Plus?

If a return type isn't specified, the C compiler assumes a default return type of int .


Video Answer


1 Answers

The syntax:

std::pair<auto, auto> f() { return std::pair(1, 2); }
~~~~~~~~~~~~~~~~~~~~~

Was part of the original Concepts TS but was not included in the Concepts proposal that is part of C++20. As such, the only placeholder types in C++20 are auto (and variations thereof like auto**), decltype(auto), and constrained placeholders (Concept auto and variations thereof). This kind of nested placeholder type would be very useful, but is not part of C++20, so that function declaration is ill-formed.

Now, gcc allows it because gcc implemented the Concepts TS and I guess they decided to keep this feature. clang never implemented the TS, so it doesn't.

Either way, this:

std::pair<auto, auto> f() { return {1, 2}; }

Would always be ill-formed. The meaning of the syntax is that we deduce the return type and then require that it matches pair<T, U> for some types T and U. We're basically trying to invoke the invented function:

template <typename T, typename U>
void __f(std::pair<T, U>);

__f({1, 2}); // this must succeed

But you cannot deduce a type from {1, 2} - a braced-init-list doesn't have a type. Perhaps this is something that should be explored (as it's easy to understand at least in a simple case like this), but it has never been allowed. So rejecting it is correct either way.

Lastly:

gcc 9.2 seems to believe that std::pair<auto, auto> is an int. How can this be explained?

For some reason (probably due to our C legacy with implicit int), when gcc does not recognize or understand a type, it just uses int as the placeholder in error messages. This is super confusing, because obviously it's gcc that came up with int and not the source code. But that's the way it is.

like image 120
Barry Avatar answered Oct 23 '22 19:10

Barry