问题
Is this not valid as C++14?
auto f = [](auto x, auto y = std::decay_t<decltype(x)>{}) { };
f(0);
I was expecting it to be roughly equivalent to
auto f = [](int x, int y) { };
f(0, int{});
Neither GCC 6.3 nor Clang 4.0 accepted my code.
- http://ideone.com/b7b4SK GCC
- http://ideone.com/EyLYaL Clang
Is it related to my lack of understanding of C++ template deduction phases? Does the 1400 pages long spec actually has an explicit answer to my question?
Update
To summarize, my problem can in fact be reduced to this piece of code (free of lambda, single parameter) and it is invalid under C++14 (thanks @BaummitAugen and @NirFriedman)
template <typename T>
void f(T x = 0) { }
int main() {
f();
}
回答1:
The compilers are correct to reject your code, it is indeed not valid C++14.
In the standard (using N4141 here) we have
For a generic lambda, the closure type has a public inline function call operator member template (14.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.
(5.1.2/4 [expr.prim.lambda]). So your call is equivalent to a call to some
template <class T1, class T2>
auto operator() (T1 x, T2 y = std::decay_t<decltype(x)>{});
Now
If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
(14.8.2/4 [temp.deduct.type]) and
The non-deduced contexts are:
[...]
- A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
(14.8.2/5 [temp.deduct.type]) makes your call ill-formed.
回答2:
I can't quote the spec, but I will quote cppreference which is an authoritative source and is often easier to read/follow. In particular, see http://en.cppreference.com/w/cpp/language/template_argument_deduction.
Non-deduced contexts
In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction...
You probably are already aware that template parameters cannot always be deduced. Going down the list of entries, we see:
4) A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done:
Which gives the following example:
template<typename T, typename F>
void f(const std::vector<T>& v, const F& comp = std::less<T>());
std::vector<std::string> v(3);
f(v);
variadic lambdas are basically equivalent to function templates with a type template parameter substituted for each usage of auto, so this example (which does not compile) is equivalent to your example.
So basically, the second type cannot be deduced because it is a non-deduced context.
This answer could probably be improved by giving a very good example of why exactly it's been decided to make this a non-deduced context, since naively it seems like it's possible. My guess is that this is basically because a function template is just that, a template for creating function. Defaulted arguments in turn, essentially create multiple callable signatures for the same function. So you can't really deal with defaulting, until you have a function, but you can't have a function until you instantiate, which requires knowing the template parameters.
It's instructive to note that this simpler example has the same issues:
template<typename T, typename F>
void f(const std::vector<T>& v, const F& comp = int{});
So the dependence on the first template parameter actually has nothing to do with the issue.
来源:https://stackoverflow.com/questions/42240849/c14-lambdas-default-argument-type-deduction-depending-on-preceding-arguments