This proposal primarily aims to remove the differences between functions, and function objects. Right now, the only reason to use functions is explicit template arguments, and because they are more convenient to specify. Else, function objects are superior in every way, as they are more convenient to use, more flexible, and can be more efficient, as function pointers can interfere with inlining and such optimizations.I propose that, in alteration of what is currently defined, the name of a function in fact refers to a function object of undefined type. When that function object is invoked, it forwards all of it's arguments to that function.
void SomeFunc(std::string str);
SomeFunc({"A String"});
void SomeFuncFwd(std::string &&str) {SomeFunc(std::move(str));}
For member functions, this is implicitly stored as a reference, not a copy. For example, consider a trivial example:template<typename T> bool less(T lhs, T rhs) { return lhs < rhs; }Currently, if you wish to pass this function as a function object to std::sort, it must be instantiated with a type at the pass site to generate a function pointer, which is often undesirable. Not only does it have the potential to interfere with inlining and other optimizations if a compiler is not sufficiently intelligent, but you have to know T in advance and explicitly specify it. A function object does not have this drawback. It would now be possible to callstd::sort(begin, end, less);
struct less
{
template<typename T> bool operator()(T lhs, T rhs) { return lhs < rhs; }
};
Here, the compiler would effectively generate a function object similar to the following:struct __some_internal_type {
template<typename... Args> auto operator()(Args&&... args) -> decltype(less(std::forward<Args>(args)...)) {return less(std::forward<Args>(args)...);}
};std::sort(begin, end, __some_internal_type());In order to preserve legacy code, a conversion to function pointers of an appropriate type must of course be specified.
void SomeFunc(int);
auto GetFunctor() -> std::function<decltype(SomeFunc)> { return {SomeFunc};}
void ThisIsAFunction();
void ThisIsAFunctor() functor;
There would be no function overloading as well, which is another thing that confounds your idea, since overloading is something that can happen in different translation units in different ways (different TLs can see different sets of functions with the same name). Type definitions can't be spread across translation units, so if you wanted to group them all in a type definition, you'd have a problem.
In C++11, decltype(SomeFunc) is a function type, and is therefore legal as a parameter to std::function. In your proposed revision, it isn't; it's an object type. Which cannot be the type provided to a std::function.
Partly, I'd suggest that not permitting elision to work through perfect forwarding is a problem, and I think the rules for elision should be relaxed.
In fact, you could argue that this would be an extra justification for permitting such a proposal.There would be no function overloading as well, which is another thing that confounds your idea, since overloading is something that can happen in different translation units in different ways (different TLs can see different sets of functions with the same name). Type definitions can't be spread across translation units, so if you wanted to group them all in a type definition, you'd have a problem.No (although this is my explanation fail). The entire point is to simplify the use of overloaded functions which currently require explicit casts and whatnot (as well as templates). After all, if I wrote the struct definition out manually, the compiler still has to deal with that. When you use the function object, it has the same effect as calling the function with those arguments at that exact place in that exact TU. If that necessitates creating multiple types for multiple usage points, then that's what's necessary- and exactly what would happen if you used a lambda instead.
hof([]min, 0, 1);
where []id-expression is a way of capturing an overload set. I haven't seen any further development of that idea, but it seems like a really nice way to simplify usage of functions as function parameters without fundamental changes.
Regards,
Gregory
You're not talking about a small compiler thing anymore. Now the compiler needs to create new type every time the function is used (or at least, frequently). This is not a tweak or a minor change; this is a fundamental rewrite of some of the most basic parts of every compiler. Not to mention the sheer amount of wording changing that needs to happen in the standard to explain how this all works.
This is a highly dangerous thing you're suggesting, which can massively break C++ in unpleasant and difficulty to detect ways.
very meaning of what a function is.
#define LIFT_FUN(name) [&]<class... Ts>(T&&... vs){ return name(std::forward<Ts>(vs)...); }
sort(f, l, LIFT_FUN(less))
.
sort(f, l, []less);
While [][] looks funny, I see no ambiguity here. So if we allow []+ and []*, we should allow [][] as well. There's ambiguity with []() - it looks like a beginning of lambda, so that should not be treated like lifted form of operator ().
Regarding the []<class>::<member> syntax, after thinking about it more, it should be probably folded in the general []id-expression; syntax. I.e:
([]id-expression)(x);should map to the first compilable of the following
x.id-expression; x.id-expression(); id-expression(x);
So, my remark was in the context of that proposal. If it would be possible then []() would be analogous to boost::apply<>.
On Saturday, November 24, 2012 1:48:41 AM UTC-8, grigor...@gmail.com wrote:While [][] looks funny, I see no ambiguity here. So if we allow []+ and []*, we should allow [][] as well. There's ambiguity with []() - it looks like a beginning of lambda, so that should not be treated like lifted form of operator ().
Grammatically, it may be unambiguous, but it is inconsistent with the rest of C++.
The syntax should be `[]<function name>`. The name of the + operator function is `operator +`. Therefore, it should be `[]operator +`. Plus, it allows you to do proper scoping where appropriate, such as `[]some_namespace::operator +`, which is already standard C++ grammar for the name of the + operator function in `some_namespace`.
While [][] looks funny, I see no ambiguity here. So if we allow []+ and []*, we should allow [][] as well....
std::sort(begin, end, (expression that yields an lvalue or rvalue x).less);
std::sort(begin, end, (expression that yields an X*)->less);