auto f = []<class T>() { ... };
f<int>(); //error expected primary expression before 'int'
f.operator()<int>(); //the only waypostfix-expression < template-parameter-listopt > ( expression-listopt )struct A { template<class T> void operator()(T&&) {} };
struct B : A { template<class T> bool operator<(T&&) { return true; } };
int main()
{
constexpr auto size_of = []<class T>() { return sizeof(T); };
static_assert(size_of<int>() == sizeof(int));
A no_lt_op;
B with_lt_op;
no_lt_op<int>(42); //ok
no_lt_op<42>(42); //try to call method, doesn't match, error
with_lt_op<int>(42); //sees operator<, doesn't try to interpret as '.operator()'
[](){}<int>(); //no templated operator() so postfix-expression is '[](){}', try to parse a relational-expression, error: expected primary-expression before 'int'
}What I propose is to update the postfix-expression part of the standard (8.2) to allow :postfix-expression < template-parameter-listopt > ( expression-listopt )iff the type of postfix-expression has a templated operator() and has no operator< .
On Mon, Sep 18, 2017 at 12:47 PM, <bastie...@gmail.com> wrote:What I propose is to update the postfix-expression part of the standard (8.2) to allow :postfix-expression < template-parameter-listopt > ( expression-listopt )iff the type of postfix-expression has a templated operator() and has no operator< .What do you mean by "has no operator<"? The operator< that is found when doing comparisons is frequently a non-member (comparisons are generally recommended to be implemented as non-member overloads, not member overloads).
On Mon, Sep 18, 2017 at 12:07 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> I don't see a great need for this.
>
> [...]
>
> So, why do we need this ability? How often do we need to create functors
> where you specify template parameters directly? Is that something we really
> want to encourage?
>
I feel this is useful, otherwise you can't
write functions like `make_unique<T>`
with lambdas -- where the type/non-type
parameters are there only for manually
input.
On Mon, Sep 18, 2017 at 12:47 PM, <bastie...@gmail.com> wrote:What I propose is to update the postfix-expression part of the standard (8.2) to allow :postfix-expression < template-parameter-listopt > ( expression-listopt )iff the type of postfix-expression has a templated operator() and has no operator< .What do you mean by "has no operator<"? The operator< that is found when doing comparisons is frequently a non-member (comparisons are generally recommended to be implemented as non-member overloads, not member overloads).
//a.hpp
template<class T, class V> constexpr bool operator<(A, B) { return true; };
//b.cpp
constexpr auto l = []<class T>() {...};
constexpr auto r_0 = l<int>(); //ok;
#include "a.hpp"
constexpr auto r_1 = l<int>(); //no longer possibletemplate<class T, class V> bool operator<(T, V) { return true;}
bool r = ([](){})<5>(10);//calls operator< with current implementation
([]<int>(int))<5>(10); //calls operator()<5>(10)template<size_t from, size_t to>
constexpr auto apply_sequence_from_to()
{
static_assert(to >= i);
return []<size_t... I>(std::index_sequence<I...>)
{
return []<template<class...> Holder>(auto&& getter)
{
return Holder {getter<(I+from)>()...};
};
}(std::make_index_sequence<to - from>{});
}
std::tuple a = {...};
decltype(a) b = {...};
auto sequence = apply_sequence_from_to<2,5>();
auto added = sequence<std::tuple>([&]<size_t I>() { return std::get<I>(a) + std::get<I>(b); });
auto numbers = sequence<std::tuple>([&]<size_t I>() { return I; });On Mon, Sep 18, 2017 at 12:21 PM, Nicol Bolas <jmck...@gmail.com> wrote:
>
> Do we need to be able to write lambdas like that? That's my question: what
> is the practical use case for this?
Yes, because lambda can capture, (template)
functions cannot.
>
> We don't need lambda functions to be able to do everything that non-lambda
> functions can do.
But we need lambda to do more things non-lambda
cannot do :) Because it can capture, so IMHO it
deserve higher priority to evolve.
//let me remind you that this is the standard way to use std::apply with an templated function
template<class... Args> void printer(Args&&...) {...}
std::apply([]<class T>(T&&... args) { printer(std::forward<Args>(args)...); }, some_tuple);3: The sender needs to have the function capture values form the local scope where the function is provided.
sequence<std::tuple>([]<size_t I>() { return I; });
constexpr auto tuple = {...};
sequence<std::tuple>([]<size_t I>() {return std::get<I>(tuple);};//no capture required for constexpr variables
4: The receiver should not call a named member of the functor. That is, it is more natural for the receiver to use `operator()` than a member with a real name.
sequence.operator()<std::tuple>([]<size_t I>() { return I; }); // is more natural for you ?
sequence.template operator()<std::tuple>([]<size_t I>() { return I; }); //how about this?struct struct_used_once_for_no_reason
{
borring_qualified_long_name &parser;
struct_used_once_for_no_reason(borring_qualified_long_name &parser) : parser(parser) {}
template <template T>
constexpr auto meaningless_name()
{
if constexpr (std::is_integral<T>) {
long long i;
return parser.readll(&i);
// bound check
return T(i);
} else if constexpr (...)
// ...
}
};
//N lines later
template<...>
auto somefunc(...)
{
...
struct_used_once_for_no_reason get_value(parser);
auto hdr = get_value.meaningless_name<int>();
//process
std::generate_n(back_inserter(v), n, get_value.meaningless_name<float>()); //other stuff, will never use get_value again
}Functions can’t capture local variables or be declared at local scope; if you need those things, prefer a lambda where possible...
constvariablesIt nicely encapsulates local initialization, including cleaning up scratch variables needed only for the initialization, without needing to create a needless nonlocal yet nonreusable function. It also works for variables that should be const but only after some initialization work...
Hi,Now that the Familiar template syntax for generic lambdas proposal has been accepted in C++20,I think it would be interesting to be able to call such lambdas (and functors) with explicit template parameters using the same syntax as for templated functions.For now we must call explicitly .operator() to give said parameters which defeats the purpose of operator().auto f = []<class T>() { ... };
f<int>(); //error expected primary expression before 'int'
f.operator()<int>(); //the only wayWhat I propose is to update the postfix-expression part of the standard (8.2) to allow :postfix-expression < template-parameter-listopt > ( expression-listopt )iff the type of postfix-expression has a templated operator() and has no operator< .
I've made a patch of 100 lines for gcc-7.2 that implements this idea and you can try a live version of the compiler here : compiler explorer.Here's an exemple:struct A { template<class T> void operator()(T&&) {} };
struct B : A { template<class T> bool operator<(T&&) { return true; } };
int main()
{
constexpr auto size_of = []<class T>() { return sizeof(T); };
static_assert(size_of<int>() == sizeof(int));
A no_lt_op;
B with_lt_op;
no_lt_op<int>(42); //ok
no_lt_op<42>(42); //try to call method, doesn't match, error
with_lt_op<int>(42); //sees operator<, doesn't try to interpret as '.operator()'
[](){}<int>(); //no templated operator() so postfix-expression is '[](){}', try to parse a relational-expression, error: expected primary-expression before 'int'
}
--
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-proposals+unsubscribe@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/8a389d85-515a-4ca8-8451-6e366f8e655e%40isocpp.org.
First thing, C++ devs never think about the pain to use their API.//let me remind you that this is the standard way to use std::apply with an templated function
template<class... Args> void printer(Args&&...) {...}
std::apply([]<class T>(T&&... args) { printer(std::forward<Args>(args)...); }, some_tuple);Which means that devs will return non-deducible templated lambdas if it allows them do something they want to do and can't otherwise (and it does).
Le lundi 18 septembre 2017 23:16:58 UTC+2, Nicol Bolas a écrit :3: The sender needs to have the function capture values form the local scope where the function is provided.no, it can if it wants to.
sequence<std::tuple>([]<size_t I>() { return I; });
constexpr auto tuple = {...};
sequence<std::tuple>([]<size_t I>() {return std::get<I>(tuple);};//no capture required for constexpr variables4: The receiver should not call a named member of the functor. That is, it is more natural for the receiver to use `operator()` than a member with a real name.A) In no way are we preventing operator() to be called explicitly.
B)sequence.operator()<std::tuple>([]<size_t I>() { return I; }); // is more natural for you ?
sequence.template operator()<std::tuple>([]<size_t I>() { return I; }); //how about this?
| Second, example b doesn't qualify, since you gain absolutely nothing by making that a lambda instead of making it a member function of the parser type or just a named struct. This is especially true considering how big that lambda is likely to be; defining it inline makes your code less readable not more readable.Yes it does.You ass-su-me that parser is a type created by the caller,You ass-su-me that get_value will be used outside of the scope local,You believe that this is more readable?You believe that this is more efficient?
On Mon, Sep 18, 2017 at 6:06 PM, Nicol Bolas <jmck...@gmail.com> wrote:
>> std::generate_n(back_inserter(v), n, get_value<float>);
>
> First, you may have confused the proposed feature here. The feature is not
> "`functor<args>` transforms itself into a functor that gets called with
> those arguments." The feature is "`functor<args>(...)` is equivalent to
> `functor.operator()<args>(...)`".
>
Start from considering whether my piece of code is
desirable to write, right?
The b) example clearly
shows that I want the data member to be shared
across the lambda instantiations, thus I'm not asking
for creating new lambda objects.
Then a follow-up question is that why `functor<args>`
can't mean `functor.operator<args>`, and then we
realize that we miss lambda's "this" pointer here, but
since lambda's member functions' addresses are
compile time constants, thus they can be a part of
the type, so we only need to pass the `this` pointers
-- and that's the basic implementation strategy.
Going further, ref captures &p are not required to
be implemented as reference members, thus we
could just passing &p if that's the only capture, one
less indirections.
> [..] defining it inline makes your code less
> readable not more readable.
>
I just want to "express ideas directly in code"
(p0559r0 A.1, also from D&E). I mean function,
then give me a function.
Functions should be short; that makes it easy to read them and reason about them. All of that `if constexpr` logic takes up a lot of room. Room that's taking up space in your function that's, again, supposed to be short. By extracting all of that complex `if constexpr` code out into its own separate place, you make the function that uses it much more digestible.
bool do_stuff(MyInt x) {return 7 + x < x(x + 1); // oops, your proposal parses this as a template argument list}Your implementation rejects this currently-valid code.
struct MyInt
{
MyInt(int);
operator int() const;
};
MyInt func();
[](auto&& x) { ... }.operator()<int>(func());
template<class T>
void do_x(T&& f)
{
f.template operator()<int>(func());}postfix-expression . < template-parameter-listopt >
postfix-expression -> < template-parameter-listopt >template<class T>
void do_x(T&& f)
{
f.<int>(func());
}Oh no, please don't invent calling syntax. I don't
feel that this problem is that hard to solve. Lambda
are the only candidates we have to work on,
and when type is known to be a generic lambda, just
allow lambda<T...>; when it's not known, the type
is dependent, either ignore the use case, or ask for
template lambda<T...> to opt-in.
For non-generic lambdas:
+without_capture -> T0
T0 = Some (*)(T...)
+with_capture -> T1
T1 = unspecified<C, &C::operator()>
For generic lambdas:
lambda<T...> -> T2
T2 = unspecified<C, &C::operator()<T...>>
T0, T1, T2 are all fundamental types and
can be called with the same syntax
f(args, to, the, lambda) where f is the
corresponding object.
--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
_______________________________________________
template<class... Lambdas>
struct select : Lambdas...
{
using Lambdas::operator()...;
template<template/*class or auto for now*/... TemplateParams> /* by the way is there such a proposal, I haven't found any ? */
constexpr auto build() //or operator()() would also work
{
return [*this]<class... Args>(Args&&...)
{
return select::template operator()<TemplateParams...>(std::forward<Args>(args)...);
};
}
};
template<class... L>
select(L&&....) -> select<L...>;
select writer =
{
[&fp]<buffer_policy x> (char* p, size_t sz)
{
writeall(fp, x | other_flags, p, sz);
// ...
},
[&fp]<buffer_policy x> (/*some other arguments*/)
{
//...
}
};
select writer = []() {}; //doesn't work though, require braced-init-list
rsync_writer rs(writer.build<buffer_policy::unbuffered>());
// ...
stream_formatter fmt(writer.build<buffer_policy::line_bufferred>());template i<int>; //i.template operator int(i);
template *i<int>; //i.template operator*<int>();
template i<int>(); //i.template operator()<int>();
Le mardi 19 septembre 2017 03:16:00 UTC+2, Richard Smith a écrit :.bool do_stuff(MyInt x) {return 7 + x < x(x + 1); // oops, your proposal parses this as a template argument list}Your implementation rejects this currently-valid code.First of all thanks for your comment.This was actually an implementation error where I did't protect against error in the attempt to parse a template id.I've updated the compiler, your code now works as supposed : compiler explorer
As for the disambiguation, you're right and this effectively kills the proposal as without disambiguation the performance cost will be too great even I ignored the other issues.That being said I still believe that simplifying the syntax call of the operator() to specify template parameter would be a good thing.One reason that I failed to bring up for instance is if we want to override the deduced type of an argument.For instance:struct MyInt
{
MyInt(int);
operator int() const;
};
MyInt func();
[](auto&& x) { ... }.operator()<int>(func());
template<class T>
void do_x(T&& f)
{
f.template operator()<int>(func());}So I thought about it and the simplest way I've found was by allowing :postfix-expression . < template-parameter-listopt >
postfix-expression -> < template-parameter-listopt >It's simpler to implement (the patch is of 44 addition, 30 suppression and mostly indentation change due to new If), no possible conflict with operator<, no issue with dependent scope, no performance cost, no ambiguity, etc..template<class T>
void do_x(T&& f)
{
f.<int>(func());
}On top of that a function can't return an unresolved templated function and can't take one in parameter,so making a templated function and a templated operator() be called the same way doesn't seem that interesting in the end.
--
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-proposals+unsubscribe@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/80747694-4a3f-4e0d-899f-2237dd0326f3%40isocpp.org.
I'm afraid that doesn't solve the problem; slightly more elaborate example:return 7 + x < x(x + 1) && x > (4 + x) * 3;Now "x<x(x+1) && x>(4 + x)" could be either a call to the lambda with template argument of x(x+1) && x, or some comparisons joined by an &&. compiler explorer
struct MyInt {
int value;
constexpr MyInt(int v) : value(v) {}
template<auto,class T> decltype(auto) constexpr operator()(T t) { return t; }
};
constexpr bool operator<(MyInt, auto&&) { return true; }
constexpr bool operator>(MyInt, auto&&) { return false; }
constexpr bool operator&&(auto&&, auto&&) { return true; }
constexpr auto do_stuff(MyInt x) {
return x < x && 1 >(x.value);
}
int main()
{
do_stuff(42) //with current standard false, with proposal 42
}
templateopt postfix-expression < template-parameter-listopt > ( expression-listopt )postfix-expression < template-parameter-listopt > ( expression-listopt )postifx-expression.templateopt operator()< template-parameter-listopt > ( expression-listopt )//template require at the front
template postfix-expression < template-parameter-listopt > ( expression-listopt )
//ex: template f<int>();
//template required before '<'
postfix-expression template < template-parameter-listopt > ( expression-listopt )
//ex: f template<int>();
//the cleanest and shortest solution
postfix-expression . < template-parameter-listopt >
postfix-expression -> < template-parameter-listopt >
//ex: f.<int>();On Wed, Sep 20, 2017 at 8:16 AM, <bastie...@gmail.com> wrote:
>
> I like it but this can be done one the library-side, without my proposal, if
> we have an unconstrained template parameter (one that non-type, type,
> template,...).
> template<class... Lambdas>
> struct select : Lambdas...
No? You may have noticed that sizeof(select)
grows linearly w.r.t. the number of the number
of overloads, while in my example
sizeof(bounded_method) is constantly a
pointer's size.
#include <utility>
#include <type_traits>
#include <functional>
#include <iostream>
template<class Lambda, bool IsEmpty = (sizeof(Lambda) <= 1)>
struct select_reference
{
Lambda *l;
constexpr select_reference(Lambda &l) : l(std::addressof(l)) {}
template<class... Args>
constexpr auto build()
{
return [&]<class... Rs>(Rs&&... args)
{
return l->template operator()<Args...>(std::forward<Rs>(args)...);
};
}
};
template<class Lambda>
struct select_reference<Lambda, true>
{
constexpr select_reference(Lambda&) {}
template<class... Args>
constexpr auto build()
{
return []<class... Rs>(Rs&&... args)
{
return static_cast<std::add_pointer_t<Lambda>>(nullptr)->
template operator()<Args...>(std::forward<Rs>(args)...);
};
}
};
template<class T>
select_reference(T&) -> select_reference<T, (sizeof(T) <= 1)>;
int main()
{
auto lambda = []<class T>() {
std::cout << sizeof(T) << std::endl;
};
select_reference s = lambda;
static_assert(sizeof(s) <= 1);
[](auto&& f)
{
static_assert(sizeof(f) <= 1);
f();
}(s.build<int>());
}And there is a usability problem to keep multiple,
isolated copies of the same member. Consider
[fd = 0]<int fcntl_flags, ...>
now one operator() instantiation modifies member
fd, the change is not reflected in another
instantiation, which is totally not the way a
member function should work.
--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
_______________________________________________
template<class... Lambdas>
struct select : Lambdas...
{
using Lambdas::operator()...;
template<template/*class or auto for now*/... TemplateParams> /* by the way is there such a proposal, I haven't found any ? */
constexpr auto build_copy() //or operator()() would also work
{
return [*this]<class... Args>(Args&&...)
{
return select::template operator()<TemplateParams...>(std::forward<Args>(args)...);
};
}
template<template... TemplateParams>
constexpr auto build_reference()
{
return [this]<class... Args>(Args&&...)Le vendredi 22 septembre 2017 01:27:27 UTC+2, Richard Smith a écrit :I'm afraid that doesn't solve the problem; slightly more elaborate example:return 7 + x < x(x + 1) && x > (4 + x) * 3;Now "x<x(x+1) && x>(4 + x)" could be either a call to the lambda with template argument of x(x+1) && x, or some comparisons joined by an &&. compiler explorerThe compile error you are seeing is actually yet another implementation issue (coming from the fact that there is no builtin way try-parse a template parameter list with gcc).