On Thursday, 20 July 2017 00:00:24 UTC+3, Manfred wrote:
> On 7/19/2017 9:56 PM, Öö Tiib wrote:
> > On Wednesday, 19 July 2017 21:47:05 UTC+3, Manfred wrote:
> >> On 7/19/2017 5:57 PM, Juha Nieminen wrote:
> >>> Manfred <non...@invalid.add> wrote:
> >>>> On 7/19/2017 10:08 AM, Alf P. Steinbach wrote:
> >>
> >> Probably so, anyway my point was more about the fact that Key is a
> >> generic data member, while Compare is a predicate, so it would make more
> >> sense for map to allow for a function type for it.
> >
> > Function is not object and so can not be copied. The std::map is very
> > desired to be assignable so how it makes sense?
>
> In the same way as std::function<> is.
Hmm. I was incorrect above. :( Comparator object of map is not copied
when map is assigned, it is set at construction and done. The allocator
may be copied (if its traits show that there is a point to). Sorry.
The real reason why standard containers and algorithms are made
differently than std::function is likely to make it easy for compiler
to optimize by inlining the calls of function objects (like predicates
or hashers passed) in containers and algorithms.
The std::function however is doing type erasure. That means in it
there are type-erased pointers. Compilers have difficulties to inline
calls thru such pointers. That is usually insignificant cost (loss of 10
nanoseconds per call on my desktop PC). However such cost can matter
on case of containers and algorithms that when working with large
data can do lot of such calls and take noticeable part of run-time.
For example that difference is what makes std::sort to be faster
(sometimes by quarter sometimes twice) than equivalent qsort.
...
> > I don't think so. Function object is object that can be called as if it
> > is a function. Therefore function is not function object since it is not
> > an object.
>
> You are right, according to how the language works, thanks!
Always happy to discuss. :) Note that some of how the language works is
legacy from past. The array/pointer and function/pointer relations are
so in C. The C++ (and the Objective-C even more) are so very careful to
keep the common subset with C as large as possible. That allows people
to who want to be picky and to use its features very conservatively to
do so.
>
> However, if you think in terms of pure metaprogramming (which Bjarne
> likes a lot), handling functions as objects is exactly why features like
> std::function<> have been introduced.
The std::function is not like std::array (whose main purpose is to fix
the inconveniences of raw array) it is way more clever beast.
Type erasure of std::function is not about metaprogramming but about
making coupling in interface as loose as possible.
> So, the way I see it, <functional> made it possible to use functions as
> if they were objects, but this is not quite how the rest of the language
> works. This is what I meant above for "non uniform" deployment.
> Beware, it may very well be that this would be too much obfuscation (see
> below), my remark here is only about theoretical semantics.
Yes, std::function is great since it allows us to be minimally intrusive in
interface dynamics. You did have it in your example:
std::map<const char*, int, std::function<bool(const char*, const char*)>> m2;
That shows that we can use it (when needed) in interface of standard
containers and algorithms. But we do not usually want a predicate or hash
of standard container to be dynamically integrated. Instead we want
optimizations like I explained before.
Little demo:
#include <iostream>
#include <functional>
#include <map>
bool myCompare(int a, int b) {return b < a;}
int main(int argc, char* argv[])
{
using FlexComparator = std::function<bool(int, int)>;
// that FlexComparator is very dynamically flexible:
FlexComparator comp;
if (argc%2 == 1) comp = std::less<int>();
else comp = myCompare;
// Note that ?: does not work in style of:
// FlexComparator c = argc%2 == 1 ? std::less<int>() : myCompare;
// it will not compile since ?: is not so dynamic as we need
// Now we can do flexible map type with FlexComparator:
using FlexMap = std::map<int, const char*, FlexComparator>;
// Note that following line will throw if we forget comp
FlexMap nums({{1, "One"}, {4, "Four"}, {2, "Two"}}, comp);
for (auto n : nums)
{
std::cout << n.first << ": " << n.second << std::endl;
}
}
Nice. But compiler can't optimize that FlexMap since compiler can not
predict what value argc will have. Therefore we must explicitly put
std::function there if we want such flexibility.