Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is auto in template parameter list in lambdas part of the standard?

Today, I stumbled across the following code snippet:

#include <utility>

int main()
{

  auto a = [](std::pair<auto, auto> value)
  {

  };

  a(std::pair<int, bool>{ 3, true });
}

http://cpp.sh/5p34

I have only one question: is this code supported by the standard?

It compiles in GCC (with -std=c++14), but not clang or Visual Studio 2015 (VC++14).

This seems like it should be part of the standard because if lambdas should have the same template support as regular functions, then this should be supported.

This seems to convert to all template types, not just std::pair.

like image 992
Russell Greene Avatar asked Sep 01 '15 01:09

Russell Greene


People also ask

What is a template template parameter in C++?

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

What is a template parameter?

In UML models, template parameters are formal parameters that once bound to actual values, called template arguments, make templates usable model elements. You can use template parameters to create general definitions of particular types of template.

Which of the following types Cannot be used for a non type template parameter?

Non-type template parameters are one of the few places in C++ where a visible distinction is made between class types and others: integral types, enum types, pointer types, point- to-member types, reference types and std::nullptr_t can all be used as non-type template parameters, but class types cannot.


2 Answers

In C++14, auto is not allowed in template arguments, whether in a lambda or not. Clang and Visual Studio are both right to reject this code.

The C++14 standard reference is [dcl.spec.auto]. The auto specifier is allowed in the following contexts:

  • In the decl-specifier-seq of a function declarator (e.g., auto f();) (paragraph 2)
  • In a conversion-function-id (i.e., an operator auto() in a class) (paragraph 2)
  • In the trailing-return-type of a function declarator (e.g., auto f() -> auto;) (paragraph 2)
  • In the decl-specifier-seq of a parameter-declaration (as one of the decl-specifiers) of a lambda (paragraph 3); -this is what allows generic lambdas to exist-
  • In the declaration of a variable at block scope or namespace scope (paragraph 4)
  • In the declaration of a for loop control variable (paragraph 4), including a range-based for loop (paragraph 5)
  • In the condition of an if or switch statement or a loop (paragraph 5)
  • In a new expression, i.e., new auto(42) (paragraph 5)
  • In the declaration of a static data member in the definition of a class (paragraph 5)

Finally,

A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.

Therefore, auto is not allowed in template parameters, since that case isn't enumerated in [dcl.spec.auto].

I don't know why gcc allows it. It might be related to Concepts Lite, but I don't know if Concepts Lite actually allows this usage. It could just be an unrelated extension that is easy to implement. I assume that

[](std::pair<auto, auto> value) { /* ... */ }

is translated into

struct __some_unique_name {
    template <typename T1, typename T2>
    auto operator()(std::pair<T1, T2> value) const { /* ... */ }
    // ...
};
like image 60
Brian Bi Avatar answered Oct 16 '22 13:10

Brian Bi


As far as I can tell this is part of concepts lite and gcc is allowing this as an extension in C++14 similar to the issue in 'auto' not allowed in function prototype with Clang although unlike the previous case using -pedantic does not produce a warning like it should for extensions.

As far as I can tell the most relevant changes from the concepts lite proposal linked above that allow this are to section 7.1.6.4 [dcl.spec.auto] and 8.3.5 [dcl.fct] ; from 7.1.6.4:

Modify paragraph 3 to allow the use of auto within the parameter type of a lambda or function.

  1. If the auto type-specifier appears as one of the decl-specifiers in the decl-specifier-seq of a parameter-declaration in a parameter type of a lambda-expression, the lambda is a generic lambda (5.1.2). [ Example:

    auto glambda = [](int i, auto a) { return i; }; // OK: a generic lambda

end example ] Similarly, if the auto type-specifier appears in a parameter type of a function declaration, the function declaration declares an abbreviated function template (8.3.5). [ Example:

void f(const auto&, int); // OK: an abbreviated function template

— end example ]

and from 8.3.5:

Add the following paragraphs after paragraph 15.

  1. An abbreviated function template is a function declaration whose parameter-type-list includes one or more placeholders (7.1.6.4, 7.1.6.5). An abbreviated function template is equivalent to a function template (14.5.6) whose template-parameter-list includes one invented templateparameter for each occurrence of a placeholder in the parameter-declaration-clause, in order of appearance. If the placeholder is designated by the auto type-specifier, then the corresponding invented template parameter is a type template-parameter. Otherwise, the placeholder is designated by a constrained-type-specifier, and the corresponding invented parameter matches the type and form of the prototype parameter (?) of the concept designated by the constrainedtype- specifier (14.9.5). The invented template-parameter is a parameter pack if the corresponding parameter-declaration declares a function parameter pack and the type of the parameter contains only one placeholder. If the prototype parameter of the designated concept declares a template parameter pack, the corresponding parameter-declaration shall declare a function parameter pack. The adjusted function parameters of an abbreviated function template are derived from the parameter-declaration-clause by replacing each occurrence of a placeholder with the name of the corresponding invented template-parameter. If the replacement of a placeholder with the name of a template parameter results in an invalid parameter declaration, the program is ill-formed. [ Example:

    template<typename T> class Vec { };
    template<typename T, typename U> class Pair { };
    
    void f1(const auto&, auto);
    void f2(Vec<auto*>...);
    void f3(auto (auto::*)(auto));
    
    template<typename T, typename U>
    void f1(const T&, U); // redeclaration of f1(const auto&, auto)
    template<typename... T>
    void f2(Vec<T*>...); // redeclaration of f2(Vec<auto*>...)
    template<typename T, typename U, typename V>
    void f3(T (U::*)(V)); // redeclaration of f3(auto (auto::*)(auto))
    

[...]

like image 2
Shafik Yaghmour Avatar answered Oct 16 '22 15:10

Shafik Yaghmour