This is indeed standard compliant. The standard specifies there must be a member operator()
, and that it has one template argument for every occurence of auto
in its paramater-declaration-clause. There is no wording that forbids providing those explicitly.
Bottom of the line: The call operator of a lambda is just a normal function (template, if generic).
For reference, the relevant standard clause:
The closure type for a non-generic lambda-expression has a public
inline function call operator (16.5.4) whose parameters and return
type are described by the lambda-expression’s
parameter-declaration-clause and trailing-return-type respectively.
For a generic lambda, the closure type has a public inline function
call operator member template (17.5.2) whose template-parameter-list
consists of one invented type template- parameter for each occurrence
of auto in the lambda’s parameter-declaration-clause, in order of
appearance. The invented type template-parameter is a parameter pack
if the corresponding parameter-declaration declares a function
parameter pack (11.3.5). The return type and function parameters of
the function call operator template are derived from the
lambda-expression’s trailing-return-type and
parameter-declaration-clause by replacing each occurrence of auto in
the decl-specifiers of the parameter-declaration-clause with the name
of the corresponding invented template-parameter.
8.1.5.1/3 [expr.prim.lambda.closure] in N4659 (C++17), emphasize mine.