C++ Logo


Advanced search

Subject: Function parameter pack not at end is non-deduced: top-level or all contexts?
From: Andrew Schepler (aschepler_at_[hidden])
Date: 2020-04-01 18:07:28

In paragraph [temp.deduct.type]/5
<https://timsong-cpp.github.io/cppwp/temp.deduct.type#5> listing the
non-deduced contexts, I think item (5.7) is somewhat unclear:

The non-deduced contexts are:

   - ...
   - A function parameter pack that does not appear at the end of the

List items (5.1) to (5.3) deal with nested-name-specifiers, decltype, and
subexpressions of a constant expression. These are typically treated as
non-deduced contexts at all levels within deduction when recursively
comparing the components of compound types.

List items (5.4) to (5.6) can only possibly apply to the initial function
parameter types when deducing for a call to a function template: they apply
when an argument is a default argument, an overloaded function, or an
*initializer-list*. These aren't possibilities when comparing P and A types
within compound types.

Item (5.7) might apply to all P and A types which are function types, since
its wording doesn't require an initial argument like (5.4) to (5.6). Or it
might apply just to the *parameter-declaration-list* of the function
template being called, when the context is a function call. The placement
after (5.4)-(5.6), and the wording "the *parameter-declaration-list*"
rather than something like "its enclosing *parameter-declaration-list*"
might be taken to hint toward the latter. (Not that "the
*parameter-declaration-list*" would be incorrect for the former meaning,
just a tiny bit unclear.)

Though it's not good if a function parameter pack followed by other
parameters is ever considered a deduced context, since then
<https://timsong-cpp.github.io/cppwp/temp.deduct.type#10> would say the
function parameter pack matches all following argument types, creating too
many function arguments in P and always causing deduction to fail:

If the *parameter-declaration* corresponding to P_i is a function parameter
pack, then the type of its *declarator-id* is compared with each remaining
parameter type in the *parameter-type-list* of A. Each comparison deduces
template arguments for subsequent positions in the template parameter packs
expanded by the function parameter pack.

Compilers don't seem to agree on this. Given the program below and at
https://godbolt.org/z/Qtvsnp :

template <typename T>
struct non_deduced_helper { using type = T; };
template <typename T>
using non_deduced = typename non_deduced_helper<T>::type;

template <typename... Ts>
void f1(void (*)(Ts..., int), Ts...) {}
template <typename... Ts>
void f2(non_deduced<void (*)(Ts..., int)>, Ts...) {}

template void f1(void (*)(float, float, int), float, float); // #1
template void f2(void (*)(float, float, int), float, float); // #2

void g(double, double, int) {}

void test() {
    f1(g, 1.0, 1.0); // #3
    f2(g, 1.0, 1.0); // #4

    using FP = void (*)(void (*)(unsigned, unsigned, int), unsigned,
    static_cast<FP>(f1); // #5
    static_cast<FP>(f2); // #6

clang++ trunk, g++ trunk, and MSVC 19.24 all accept the uses of f2 (labeled
#2, #4, #6) where the function pointer type is forced to be a non-deduced

clang++ accepts the f1 function call (#3), but rejects the explicit
instantiation (#1) and initialization from function template (#5).
MSVC accepts the explicit instantiation (#1), but rejects the function call
(#3) and initialization from function template (#5).
g++ rejects all three uses of f1.

I'm not really sure what those compiler results say, other than this is a
tricky corner of the language.

Is this worth a Defect Report? (Or related to an existing one I didn't

STD-DISCUSSION list run by std-discussion-owner@lists.isocpp.org

Older Archives on Google Groups