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

сv-qualified function type and type deduction

54 views
Skip to first unread message

wander

unread,
Feb 19, 2016, 9:46:17 AM2/19/16
to
Good day!
I came across an interesting feature in the behavior of popular compilers and would like to understand the reasons.
There's a simple code:

#include <iostream>

template <typename F>
void (* declval() ) (F);

template <typename X>
char (& probe( void(*) ( X * ) ) )[1];

template <typename X>
char (& probe( void(*) ( X ) ) )[2];

int main()
{
std::cout << sizeof( probe(declval<void() const>()) );
}

The question is about type deduction. If you run this code on
* compiler GCC 4.8.x (or later)
* compiler Clang 3.4 (or later)
* compiler in the Visual Stuido (tested on VS 2013 Community)
* compiler Intel ICC 13.0.1

it will display the number "1". And I thought it is logical, since function type, being substituted argument in another function, acquires the properties of a pointer type.
On newer versions of compilers mentioned (except for VS and ICC) behavior has changed and now this code for Clang 3.5 (or higher), GCC 4.9 (or higher) displays the number "2". If you remove the second overload, then the mentioned versions of GCC and Clang error occurs:
"Forming pointer to qualified function type"
I understand why but doubt whether this rule applies in this context.

Maybe together we can find the truth.
Thank you.
Message has been deleted

Alf P. Steinbach

unread,
Feb 19, 2016, 11:00:45 AM2/19/16
to
On 2/19/2016 3:46 PM, wander wrote:
> Good day!

Yes, and a good day to u2! :)


> I came across an interesting feature in the behavior of popular
> compilers and would like to understand the reasons. There's a simple
> code:
>
> #include <iostream>
>
> template <typename F>
> void (* declval() ) (F);
>
> template <typename X>
> char (& probe( void(*) ( X * ) ) )[1];
>
> template <typename X>
> char (& probe( void(*) ( X ) ) )[2];
>
> int main()
> {
> std::cout << sizeof( probe(declval<void() const>()) );
> }
>
> The question is about type deduction. If you run this code on
> * compiler GCC 4.8.x (or later)
> * compiler Clang 3.4 (or later)
> * compiler in the Visual Stuido (tested on VS 2013 Community)
> * compiler Intel ICC 13.0.1
>
> it will display the number "1".

Huh, that's weird.

The argument type F is not a pointer.

It should not match a pointer, as indicated by result "1".


> And I thought it is logical, since function type, being substituted
> argument in another function, acquires the properties of a pointer
> type.

Oh.

Well, yes, now that you mention it, argument type F should decay to
pointer to function.

Yes.


> On newer versions of compilers mentioned (except for VS and
> ICC) behavior has changed and now this code for Clang 3.5 (or
> higher), GCC 4.9 (or higher) displays the number "2". If you remove
> the second overload, then the mentioned versions of GCC and Clang
> error occurs: "Forming pointer to qualified function type" I
> understand why but doubt whether this rule applies in this context.

Dunno. What's that rule? I could search the standard or just google it,
but I think that the one who asks should provide such info. :)


> Maybe together we can find the truth. Thank you.

Well, whatever this code is meant to support, it can surely be expressed
in some less ambiguous and more clear way?

I'd also change the name `declval` to something not used by the standard
library, or at least put this in a namespace of its own.

In passing, is that `const` really well formed? It's not long ago since
I disabled a sillywarning from MSVC about `const` on function having no
effect, which popped for their own standard library implementation...
Still I feel not quite certain that it's ignored, because when I changed
this to function pointer the compiler complained about it.


Cheers & hth.,

- Alf

wander

unread,
Feb 19, 2016, 11:45:38 AM2/19/16
to
> > ...
> > #include <iostream>
> >
> > template <typename F>
> > void (* declval() ) (F);
> >
> > template <typename X>
> > char (& probe( void(*) ( X * ) ) )[1];
> >
> > template <typename X>
> > char (& probe( void(*) ( X ) ) )[2];
> >
> > int main()
> > {
> > std::cout << sizeof( probe(declval<void() const>()) );
> > }
> > ...
> > it will display the number "1".
>
> Huh, that's weird.
>
> The argument type F is not a pointer.
>
> It should not match a pointer, as indicated by result "1".

There is online test (prints 1 on gcc 4.8.2): http://melpon.org/wandbox/permlink/1NDBIzVwEFbOn9UY
And second test (prints 2 on gcc 4.9): http://melpon.org/wandbox/permlink/PN721vPquuUtrAfH

> > If you remove
> > the second overload, then the mentioned versions of GCC and Clang
> > error occurs: "Forming pointer to qualified function type" I
> > understand why but doubt whether this rule applies in this context.
>
> Dunno. What's that rule? I could search the standard or just google it,
> but I think that the one who asks should provide such info. :)

In the new standard there is 8.3.1/4 (last draft):
"Forming a pointer to function type is ill-formed if the function type has cv-qualifiers or a ref-qualifier."

But I'm also interested in the discussion of this in the context of C++03.
In C++03 does not say so just as explicitly, and I need some help to formulate correct conclusions.

8.3.5/7 (std c++03):
"A typedef of a function type whose declarator includes a cv-qualifier-seq shall be used only to declare the function type for a nonstatic member function, to declare the function type to which a pointer to member refers, or to declare the top-level function type of another function typedef declaration."

>
> > Maybe together we can find the truth. Thank you.
>
> Well, whatever this code is meant to support, it can surely be expressed
> in some less ambiguous and more clear way?

No. It is theoretical question. I just want to understand what is happening: if I should, for example, send a bug to VS support, or vice versa, to the clang or gcc.

> I'd also change the name `declval` to something not used by the standard
> library, or at least put this in a namespace of its own.

`declval` name plays no role in this context. The examples in the online compiler, I have changed its name to the `test`.

> In passing, is that `const` really well formed?

Actually - yes. See quote from the Standard.

wander

unread,
Feb 20, 2016, 9:48:53 AM2/20/16
to
Good day!
So, let me a little bit simplify the question, making it more concrete.

Let`s see:
template <typename T>
struct test
{
typedef void(* type)(T);
};

typedef void foo_t() const;

typedef test<foo_t>::type func_t;

Is this code is correct according the standard C++11(14)?
I mean that there are two rules that now (in my opinion) contradict each other.

The first rule is (last draft):
8.3.5/5
"The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator.
After determining the type of each parameter, any parameter of type "array of T" or "function returning T" is
adjusted to be "pointer to T" or "pointer to function returning T", respectively."

The second rule is (last draft):
8.3.1/4
"Forming a pointer to function type is ill-formed if the function type has cv-qualifiers or a ref-qualifier."

This question has arisen because that all modern compilers can add a pointer to cv-qualified function type despite
the prohibition without any problems.
Example(clang): http://melpon.org/wandbox/permlink/79tqlLHKIkSdamtv
Example(vs13): http://rextester.com/RWX2579
Example(gcc): http://melpon.org/wandbox/permlink/PlGbEzpWCjSeapuw

Maybe there is some kind of exception?
Background of my initial question somehow comes to this.

Also interested in the same question in the context of C++03, where there is no equally clear statements about this case.

Thank you again.

Öö Tiib

unread,
Feb 20, 2016, 1:57:13 PM2/20/16
to
On Saturday, 20 February 2016 16:48:53 UTC+2, wander wrote:
> Good day!
> So, let me a little bit simplify the question, making it more concrete.
>
> Let`s see:
> template <typename T>
> struct test
> {
> typedef void(* type)(T);
> };
>
> typedef void foo_t() const;
>
> typedef test<foo_t>::type func_t;
>
> Is this code is correct according the standard C++11(14)?

It likely is not. The 'foo_t' there is sort of half-type.
A function type with a cv-qualifier-seq or a ref-qualifier (including a
type named by typedef-name (7.1.3, 14.1)) shall appear only as:

the function type for a non-static member function,
the function type to which a pointer to member refers,
the top-level function type of a function typedef declaration or alias-declaration,
the type-id in the default argument of a type-parameter (14.1), or
the type-id of a template-argument for a type-parameter (14.3.1).

The 'func_t' however is pointer to function type where it seems to appear
as type of function parameter. I do not understand how such function
type is valid.


> I mean that there are two rules that now (in my opinion) contradict each other.
>
> The first rule is (last draft):
> 8.3.5/5
> "The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator.
> After determining the type of each parameter, any parameter of type "array of T" or "function returning T" is
> adjusted to be "pointer to T" or "pointer to function returning T", respectively."
>
> The second rule is (last draft):
> 8.3.1/4
> "Forming a pointer to function type is ill-formed if the function type has cv-qualifiers or a ref-qualifier."

Your function type to what you form a pointer does not have cv-qualifiers
or ref-qualifiers, it has a parameter that is function type with
cv-qualifier.

>
> This question has arisen because that all modern compilers can add a pointer to cv-qualified function type despite
> the prohibition without any problems.
> Example(clang): http://melpon.org/wandbox/permlink/79tqlLHKIkSdamtv
> Example(vs13): http://rextester.com/RWX2579
> Example(gcc): http://melpon.org/wandbox/permlink/PlGbEzpWCjSeapuw
>
> Maybe there is some kind of exception?
> Background of my initial question somehow comes to this.
>
> Also interested in the same question in the context of C++03, where there is no equally clear statements about this case.
>
> Thank you again.

Not sure, may be they are preparing some sort of concepts lite with what
what you are trying to do there makes sense somehow. Or it may be I am
confused and misunderstand something obvious.

wander

unread,
Feb 20, 2016, 3:16:14 PM2/20/16
to
> The 'func_t' however is pointer to function type where it seems to appear
> as type of function parameter. I do not understand how such function
> type is valid.

I understand your point.

> > The first rule is (last draft):
> > 8.3.5/5
> > "The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator.
> > After determining the type of each parameter, any parameter of type "array of T" or "function returning T" is
> > adjusted to be "pointer to T" or "pointer to function returning T", respectively."
> >
> > The second rule is (last draft):
> > 8.3.1/4
> > "Forming a pointer to function type is ill-formed if the function type has cv-qualifiers or a ref-qualifier."
>
> Your function type to what you form a pointer does not have cv-qualifiers
> or ref-qualifiers, it has a parameter that is function type with
> cv-qualifier.
>

I know. 8.3.5/5 says that the function type acquires pointer properties (as an argument of another function), but function type with cv-qualifier-seq can not be a pointer itself. At least the user can't do it (adding pointer explicitly won't compile).
So you say code posted before is not valid. And it should not compile. But it's compiled anywhere. That's why I think there are some non-trivial exception. Or we have a bug in all compilers?

Thanks for response.

Öö Tiib

unread,
Feb 21, 2016, 2:54:03 PM2/21/16
to
On Saturday, 20 February 2016 22:16:14 UTC+2, wander wrote:
>
> So you say code posted before is not valid. And it should not compile. But
> it's compiled anywhere. That's why I think there are some non-trivial
> exception. Or we have a bug in all compilers?

For me 'func_t' is invalid type. Playing around with code in attempt
to make the compiler to express what the type of argument is reveals
things like 'void (*)() __attribute__((const))' or 'void (*)() const'
that is illegal. So either there is some dim area or controversy in
standard that lets compilers not to diagnose it or all compilers are
defective.

wander

unread,
Feb 21, 2016, 3:14:40 PM2/21/16
to
> For me 'func_t' is invalid type. Playing around with code in attempt
> to make the compiler to express what the type of argument is reveals
> things like 'void (*)() __attribute__((const))' or 'void (*)() const'
> that is illegal. So either there is some dim area or controversy in
> standard that lets compilers not to diagnose it or all compilers are
> defective.

I agree.
Thank you.
0 new messages