Protecting a function parameter pack from further deduction

81 views
Skip to first unread message

Johannes Schaub

unread,
Aug 22, 2016, 4:21:25 PM8/22/16
to std-pr...@isocpp.org
Sometimes I would like to say "this function parameter pack must
either be all-explicitly-specified or all deduced", to forbid the
following and other (similar to how "nondeduced<..>::type" can disable
deduction for non-packs)

template<typename ...T>
void f(T ...);

f<int>(1, 2); // oops

This would come handy in class template arg deduction for std::tuple,
because "std::tuple<int> a(1, 2)" could be rejected. I would propose
"protected ..." to be a "non-extensible pack expansion":

template<typename ...T>
void f(T protected ...);

f<int>(1, 2); // error!

Similar, tuple's constructor can be defined as

tuple(Types && protected ... values);

Do you like the idea?

TONGARI J

unread,
Aug 22, 2016, 9:16:09 PM8/22/16
to ISO C++ Standard - Future Proposals
You have it already:
template<class T>
struct nodeduced
{
   
using type = T;
};

template<class T>
using nodeduced_t = typename nodeduced<T>::type;

template<typename ...T, class...Z>
void f(T ..., nodeduced_t<Z>...) {}

And "std::tuple<int> a(1, 2)" is already rejected, I don't see your point.

Johannes Schaub

unread,
Aug 23, 2016, 3:00:29 AM8/23/16
to std-pr...@isocpp.org
Unfortunately, "nondeduced" does not work here, because the pack
expansion is outside of it. Compilers appear to expand the pack before
deducing the elements:


template<typename ...T, class Z>
void f(void(nodeduced_t<T>..., Z)) { }

void g(int, char) { }

int main() {
f<int>(g);
}

This will not compile, apparently compilers create "void(int,
nondeduced_t<T>, Z)" and try to compare "nondeduced_t<T>" against
"char". My "protected" prevents this "growing" of the parameter pack.

The case for "tuple<int>" refers to template argument deduction for
class templates, i.e "std::tuple t(10)". The problem with partially
specifying template arguments is that the deduction guide provides

template<Outer ...T = <int, ...>>
tuple(<int, Outer> &&... t);

And will extend "Outer" with whatever constructor arguments you pass
in. The "protected" would forbid this analog here. Perhaps here we can
put a workaround in terms of a trailing parameter pack, and put a
condition that causes an SFINAE check failure.. This would cause
deduction guides to not extend "Outer", but put the further args into
"SfinareCheck", later SFINAE-failing and not doing that deduction
(would that work??). But that's far from elegant, so I guess this is
not an option

template<typename ...SfinaeCheck, typename
std::enable_if<sizeof...(SfinaeCheck)==0,int>::type=0>
tuple(Outer &&... outers, SfinaeCheck &&...);

Apart from that, the workaround wouldn't work for the function pointer
case above anyway.
> --
> You received this message because you are subscribed to the Google Groups
> "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to std-proposal...@isocpp.org.
> To post to this group, send email to std-pr...@isocpp.org.
> To view this discussion on the web visit
> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6dda12e3-acbd-40ab-ae5d-afe538fbf308%40isocpp.org.

TONGARI J

unread,
Aug 23, 2016, 5:46:36 AM8/23/16
to ISO C++ Standard - Future Proposals
On Tuesday, August 23, 2016 at 3:00:29 PM UTC+8, Johannes Schaub wrote:
Unfortunately, "nondeduced" does not work here, because the pack
expansion is outside of it. Compilers appear to expand the pack before
deducing the elements:


template<typename ...T, class Z>
void f(void(nodeduced_t<T>..., Z)) { }

void g(int, char) { }

int main() {
   f<int>(g);
}

This will not compile, apparently compilers create "void(int,
nondeduced_t<T>, Z)" and try to compare "nondeduced_t<T>" against
"char". My "protected" prevents this "growing" of the parameter pack.

What are you trying to do with this example?
Doesn't the code I show you already prevent the growing of the parameter pack?

template<typename ...T, class... Z>
void f(void(T..., nodeduced_t<Z>...)) { }



void g(int, char) { }


int main() {

   f
<int>(g); // error, partially specified

   f
<int, char>(g); // ok, fully specified
}
 
The case for "tuple<int>" refers to template argument deduction for
class templates, i.e "std::tuple t(10)". The problem with partially
specifying template arguments is that the deduction guide provides

    template<Outer ...T = <int, ...>>
    tuple(<int, Outer> &&... t);

I haven't seen this syntax before and I believe it's not valid C++17.
Template argument deduction for constructors cannot be partially specified. There's no problem need to be solved.

TONGARI J

unread,
Aug 23, 2016, 9:29:00 AM8/23/16
to ISO C++ Standard - Future Proposals
On Tuesday, August 23, 2016 at 5:46:36 PM UTC+8, TONGARI J wrote:
On Tuesday, August 23, 2016 at 3:00:29 PM UTC+8, Johannes Schaub wrote:
Unfortunately, "nondeduced" does not work here, because the pack
expansion is outside of it. Compilers appear to expand the pack before
deducing the elements:


template<typename ...T, class Z>
void f(void(nodeduced_t<T>..., Z)) { }

void g(int, char) { }

int main() {
   f<int>(g);
}

This will not compile, apparently compilers create "void(int,
nondeduced_t<T>, Z)" and try to compare "nondeduced_t<T>" against
"char". My "protected" prevents this "growing" of the parameter pack.

What are you trying to do with this example?

Ah, so you want to deduce `Z` but have `T...` explicitly specified in that example, right?
It's strange that the following works but the above example doesn't...`Z` somehow becomes non-deducible.
template<typename ...T>
void f(void(nodeduced_t<T>..., char)) { }

Johannes Schaub

unread,
Aug 23, 2016, 9:32:06 AM8/23/16
to std-pr...@isocpp.org
This works because you never try to call "f" with anything.
Reply all
Reply to author
Forward
0 new messages