问题
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
.
回答1:
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
orswitch
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
ordecltype(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 { /* ... */ }
// ...
};
回答2:
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.
If the auto type-specifier appears
as one of the decl-specifiers in the decl-specifier-seq of a parameter-declarationin 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.
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))
[...]
来源:https://stackoverflow.com/questions/32322255/is-auto-in-template-parameter-list-in-lambdas-part-of-the-standard