Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Difference between type(type) and type(*)(type)

64 views
Skip to first unread message

Juha Nieminen

unread,
Jul 19, 2017, 3:11:28 AM7/19/17
to
Could someone explain to me in detai the exact difference between the
syntaxes "type(type)" and "type(*)(type)"? It's still unclear to me.

The difference is illustrated with this example:

std::function<bool(const char*, const char*)> f1; // ok
std::function<bool(*)(const char*, const char*)> f2 // error

std::map<const char*, int, bool(*)(const char*, const char*)> m1; // ok
std::map<const char*, int, bool(const char*, const char*)> m2; // error

Alf P. Steinbach

unread,
Jul 19, 2017, 4:08:47 AM7/19/17
to
On 19.07.2017 09:11, Juha Nieminen wrote:
> Could someone explain to me in detai the exact difference between the
> syntaxes "type(type)" and "type(*)(type)"? It's still unclear to me.

The first is a function type. Call that F. The second is the type
pointer to function, i.e. F*. A function type can also be used to form a
reference to function type, F& (and although it's unconventional to pass
raw functions by reference it guarantees that if the code is valid you
will never get a nullpointer, so no need to check that, which is nice).

With post-C11 C++ notation you can name F and F_ptr like this:

using F = auto(type) -> type;
using F_ptr = F*;

With C++03 notation you can name F and F_ptr like this:

typedef type F(type);
typedef F* F_ptr;


> The difference is illustrated with this example:
>
> std::function<bool(const char*, const char*)> f1; // ok
> std::function<bool(*)(const char*, const char*)> f2 // error

`std::function` needs a function type, not a function pointer type.

If all you have is a function pointer type then dereference it and use
`decltype` on the result.

I think `std::function` should just have accepted both function type and
function pointer type, but, that's not how it turned out.


> std::map<const char*, int, bool(*)(const char*, const char*)> m1; // ok
> std::map<const char*, int, bool(const char*, const char*)> m2; // error

A function type is a non-instantiable type, like `void`. Well not quite
(`void` is an incomplete type, and `void*` is a different /kind/ of
pointer than `F*`, not formally compatible in C++03, and only optionally
compatible in C++11 and later, to support Posix) but you get the
picture, I hope. So, you can't use a function type to declare a data
member, such as, here, the key in a `std::map`'s value type `std::pair`.


Cheers & hth.,

- Alf

Alf P. Steinbach

unread,
Jul 19, 2017, 4:11:17 AM7/19/17
to
On 19.07.2017 10:08, Alf P. Steinbach wrote:
> member, such as, here, the key in a `std::map`'s value type `std::pair`.

I mean value, not key, sorry.

(Mumbles something about eye-sight and various stuff.)


Cheers!,

- Alf

Manfred

unread,
Jul 19, 2017, 7:07:32 AM7/19/17
to
On 7/19/2017 10:08 AM, Alf P. Steinbach wrote:
>> std::map<const char*, int, bool(*)(const char*, const char*)> m1; // ok
>> std::map<const char*, int, bool(const char*, const char*)> m2; // error
>
> A function type is a non-instantiable type, like `void`. Well not quite
> (`void` is an incomplete type, and `void*` is a different /kind/ of
> pointer than `F*`, not formally compatible in C++03, and only optionally
> compatible in C++11 and later, to support Posix) but you get the
> picture, I hope. So, you can't use a function type to declare a data
> member, such as, here, the key in a `std::map`'s value type `std::pair`.

// 23.4.4, class template map
template <class Key, class T, class Compare = default_order_t<Key>,
class Allocator = allocator<pair<const Key, T>>>

The function pointer here would be the "Compare" template argument.

Alf P. Steinbach

unread,
Jul 19, 2017, 7:56:46 AM7/19/17
to
Thanks. I'm ordering new eyes as I type.

Cheers!,

- Alf

Juha Nieminen

unread,
Jul 19, 2017, 11:57:32 AM7/19/17
to
I suppose Alf's answer was correct in the sense that a typical
implementation of std::map will instantiate the comparator type as
a member variable (for example the implementation in libc++ does this,
and I'm assuming the one in libstdc++ does too). It assigns the value
it gets as a constructor parameter there (if one is given).

I suppose a "function type" simply is a special type that cannot be
instantiated by itself (even though normally types can be).

Manfred

unread,
Jul 19, 2017, 2:47:05 PM7/19/17
to
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.
It doesn't, so it is a bit less trivial than it looks.
In fact, with respect to the OP, the following works:
> std::map<const char*, int, bool(const char*, const char*)> m2; // error
std::map<const char*, int, std::function<bool(const char*, const
char*)>> m2; // works

It looks that the type(type) syntax, which was introduced in C++11 as
/call signature/ (20.14.1) to identify a function type, has not been
deployed completely and uniformly throughout the language where a
function object type is expected - thus the need for the std::function
wrapper.

Öö Tiib

unread,
Jul 19, 2017, 3:56:32 PM7/19/17
to
Function is not object and so can not be copied. The std::map is very
desired to be assignable so how it makes sense?

> It doesn't, so it is a bit less trivial than it looks.
> In fact, with respect to the OP, the following works:
> > std::map<const char*, int, bool(const char*, const char*)> m2; // error
> std::map<const char*, int, std::function<bool(const char*, const
> char*)>> m2; // works
>
> It looks that the type(type) syntax, which was introduced in C++11 as
> /call signature/ (20.14.1) to identify a function type, has not been
> deployed completely and uniformly throughout the language where a
> function object type is expected - thus the need for the std::function
> wrapper.

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.

Pointer to function however (if actually made to point at a function)
is function object since it is object and can be called like a function.

The confusion here can be similar as with array and the pointer to its
first element. Every time we try to use a function to something it
slyly tries to convert to function pointer. About like array tries to
convert to pointer of its first element on first chance.

#include <iostream>

using Function = void(int);// function type

Function function; // declaration of function with external linkage

void function(int i) // definition of that function
{
std::cout << i << std::endl;
}

int main()
{
Function x; // again a declaration of function with external linkage
// x = function; <- no way! bonus points for obfuscation go to gcc :D

Function* function_pointer = nullptr; // function pointer is object
function_pointer = function; // function converted to pointer
void (*other)(int); //
other = function_pointer; // assignable to other object
other(42); // and callable like function
}

Manfred

unread,
Jul 19, 2017, 5:00:24 PM7/19/17
to
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.

>
>> It doesn't, so it is a bit less trivial than it looks.
>> In fact, with respect to the OP, the following works:
>>> std::map<const char*, int, bool(const char*, const char*)> m2; // error
>> std::map<const char*, int, std::function<bool(const char*, const
>> char*)>> m2; // works
>>
>> It looks that the type(type) syntax, which was introduced in C++11 as
>> /call signature/ (20.14.1) to identify a function type, has not been
>> deployed completely and uniformly throughout the language where a
>> function object type is expected - thus the need for the std::function
>> wrapper.
>
> 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!

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.
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.

>
> Pointer to function however (if actually made to point at a function)
> is function object since it is object and can be called like a function.
>
> The confusion here can be similar as with array and the pointer to its
> first element. Every time we try to use a function to something it
> slyly tries to convert to function pointer. About like array tries to
> convert to pointer of its first element on first chance.
Probably so, or (like Alf also pointed out) similar to references, that
must be initialized, and cannot be assigned (and in fact Bjarne says
that references are not objects).

>
> #include <iostream>
>
> using Function = void(int);// function type
>
> Function function; // declaration of function with external linkage
>
> void function(int i) // definition of that function
> {
> std::cout << i << std::endl;
> }
>
> int main()
> {
> Function x; // again a declaration of function with external linkage
> // x = function; <- no way! bonus points for obfuscation go to gcc :D
Thanks again for the good example (and for the error message above :)

Alf P. Steinbach

unread,
Jul 19, 2017, 11:33:28 PM7/19/17
to
On 19.07.2017 20:46, Manfred wrote:
> [snip]
> It looks that the type(type) syntax, which was introduced in C++11 as
> /call signature/ (20.14.1) to identify a function type,

Oh, the `type(type)` syntax is as old as C++, mostly.

When you declare formal argument of type `type(type)`, then it decays to
pointer to function, just as an array type decays to pointer to item.

And that's been so wrt. the standard, since C++98.


Cheers!,

- Alf

Ben Bacarisse

unread,
Jul 20, 2017, 5:50:12 AM7/20/17
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:

> On 19.07.2017 20:46, Manfred wrote:
>> [snip]
>> It looks that the type(type) syntax, which was introduced in C++11 as /call signature/ (20.14.1) to identify a function type,
>
> Oh, the `type(type)` syntax is as old as C++, mostly.

The syntax for a function type is in pre-ANSI C (often called K&R C) but
since there are no prototypes in K&R C the only valid form is type().

> When you declare formal argument of type `type(type)`, then it decays
> to pointer to function, just as an array type decays to pointer to
> item.

The use in a parameter list, and the implicit type adjustment, are also
in ANSI C (1989) but it's not clear if C++ got it from ANSI C or ANSI C
got it from proto-C++. After all, the whole idea of function prototypes
came from the experiments being done with C++ at Bell Labs.

K&R is silent on the question of whether a parameter can be declared
with a function type and what, if any, the semantic of that are.

<snip>
--
Ben.

Öö Tiib

unread,
Jul 20, 2017, 8:03:56 AM7/20/17
to
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.

Manfred

unread,
Jul 20, 2017, 10:46:24 AM7/20/17
to
Great!
Never used it before C++11 (always used good old function pointers)
I learnt a bit of history today, thanks!

>
>
> Cheers!,
>
> - Alf

Öö Tiib

unread,
Jul 20, 2017, 11:00:28 AM7/20/17
to
On Thursday, 20 July 2017 15:03:56 UTC+3, Öö Tiib wrote:
>
> 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.

Oh scratch it. Seems that I was still correct on first time and it is
copied, just can't find it in standard.

Tim Rentsch

unread,
Jul 23, 2017, 1:28:13 PM7/23/17
to
Ben Bacarisse <ben.u...@bsb.me.uk> writes:

> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>
>> On 19.07.2017 20:46, Manfred wrote:
>>
>>> [snip]
>>> It looks that the type(type) syntax, which was introduced in C++11 as
>>> /call signature/ (20.14.1) to identify a function type,
>>
>> Oh, the `type(type)` syntax is as old as C++, mostly.
>
> The syntax for a function type is in pre-ANSI C (often called K&R C) but
> since there are no prototypes in K&R C the only valid form is type().
>
>> When you declare formal argument of type `type(type)`, then it decays
>> to pointer to function, just as an array type decays to pointer to
>> item.
>
> The use in a parameter list, and the implicit type adjustment, are also
> in ANSI C (1989) but it's not clear if C++ got it from ANSI C or ANSI C
> got it from proto-C++. [...]

According to this recounting of history

http://www.stroustrup.com/hopl2.pdf

the flow is from pre-C++ to ANSI C:

"The C with Classes syntax and rules, the ones subsequently
adopted for the ANSI C standard, simply appeared fully
formed in the first C with Classes implementation."

as mentioned in section 2.4.3, "Static Type Checking".

woodb...@gmail.com

unread,
Jul 23, 2017, 4:19:44 PM7/23/17
to
On Sunday, July 23, 2017 at 12:28:13 PM UTC-5, Tim Rentsch wrote:
> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
> >
> > The syntax for a function type is in pre-ANSI C (often called K&R C) but
> > since there are no prototypes in K&R C the only valid form is type().
> >
> >> When you declare formal argument of type `type(type)`, then it decays
> >> to pointer to function, just as an array type decays to pointer to
> >> item.
> >
> > The use in a parameter list, and the implicit type adjustment, are also
> > in ANSI C (1989) but it's not clear if C++ got it from ANSI C or ANSI C
> > got it from proto-C++. [...]
>
> According to this recounting of history
>
> http://www.stroustrup.com/hopl2.pdf
>
> the flow is from pre-C++ to ANSI C:
>
> "The C with Classes syntax and rules, the ones subsequently
> adopted for the ANSI C standard, simply appeared fully
> formed in the first C with Classes implementation."
>
> as mentioned in section 2.4.3, "Static Type Checking".

I think that's basically right, but might argue with the
"fully formed" part. I think Stroustrup is better when he
says things like he had to "hurry up and fix everything"
when C++ started getting popular.

Anyway, I hope something similar to this will happen with
some of the features from C++ 2014 and 2017 being back-ported
to C++ 2011. In particular, I hope string_view and
make_unique will be back-ported. I'd also like the new
version of emplace_back. These are library related items
so I think that makes things a little easier to handle.



Brian
Ebenezer Enterprises - In G-d we trust.
http://webEbenezer.net

Öö Tiib

unread,
Jul 23, 2017, 5:27:08 PM7/23/17
to
Brian, you keep talking about that dream of yours about back-porting
features to C++11 but never tell what that technically is supposed
to mean.

Currently there are no programming languages named C++11 and C++14.
There is programming language named C++ specified by its standard
ISO/IEC 14882.

According to web-page of publisher of that standard ISO
https://www.iso.org/standard/50372.html that C++11 is revision of
"ISO/IEC 14882" standard "ISO/IEC 14882:2011" that has been already
withdrawn as revised by next revision "ISO/IEC 14882:2014" (often called
as C++14) at page https://www.iso.org/standard/64029.html
That revision is published current revision of C++ standard.

So what is that "back-port" and how you want it to happen? Do you want
the withdrawal to be undone and the revisions of C++ to be turned into
family of similar programming languages? What is the point of it? What
is the alleged benefit?

Real Troll

unread,
Jul 23, 2017, 5:47:08 PM7/23/17
to
On 23/07/2017 22:26, Öö Tiib wrote:
> What is the alleged benefit?

He doesn't need to learn anything new!! He might be nearing end of life
so he might as well not bother to learn anything new or anything new
ways of doing things.

woodb...@gmail.com

unread,
Jul 23, 2017, 7:12:43 PM7/23/17
to
Perhaps C++ 2017 is to C++ 2011 as C++ 1998 is to C. I don't
know if the technical specifications would need to change.
I would simply like compiler vendors to do this for their
C++ 2011 compilers.

The point is to increase the number of people who can use
my software. Otherwise those on "C++ 2017 island" are just
waiting for more people to join them. Lots of people can't
make it to the island, but they would be glad to get some
C++ 2017 features. Recall also my point that C++ 2011 was
both late and immature when it did finally arrive.


Brian
Ebenezer Enterprises
http:webEbenezer.net

Öö Tiib

unread,
Jul 23, 2017, 7:59:16 PM7/23/17
to
There are no such programming languages like "C++ 2011". Period.

> I would simply like compiler vendors to do this for their
> C++ 2011 compilers.

What compiler vendor has releases such things named "C++ 2011 compiler"?
I know none such vendors. I see no indications nor reasons why they will
ever do that.

> The point is to increase the number of people who can use
> my software. Otherwise those on "C++ 2017 island" are just
> waiting for more people to join them. Lots of people can't
> make it to the island, but they would be glad to get some
> C++ 2017 features. Recall also my point that C++ 2011 was
> both late and immature when it did finally arrive.

OK, lets have thought experiment and imagine that for example Microsoft
suddenly released a thing named "Microsoft C++ 2011 compiler" and
"back-ported" exactly the features you want from current C++ and from
C++1y drafts into it. Impossible but lets fantasize.

There are guys who use use lets say "Visual Studio 2012". If they do not
want to migrate to "Visual Studio 2015" then why they migrate to that
"C++ 2011 compiler" abomination?

0 new messages