type function U[1] and U(*)[1] SFINAE issue

F

Fei Liu

Hello, I just hit a strange problem regarding SFINAE. The following code
causes compile error (void cannot be array element type), I thought
SFINA should match test(...) version instead and not issue any error.

If I replace U[1] with U(*)[1], then the code compiles again. I couldn't
make the sense out of it. What's the magic with (*)? Note that
function<int>::yes returns correct result 0 even with U[1].

Please help me out.

Fei

#include <iostream>
using namespace std;

template <typename T>
struct function{

template <typename U> static char test(...);
template <typename U> static char (&test(U[1]))[2];

enum { yes = (sizeof(function<T>::test<T>(0)) == 1) };
};

int main(){

cout << "int: " << function<int>::yes << endl;
cout << "void: " << function<void>::yes << endl;
// function
cout << "void(): " << function<void()>::yes << endl;
// function ptr
cout << "void(*)(): " << function<void(*)()>::yes << endl;
}
 
B

Barry

Hello, I just hit a strange problem regarding SFINAE. The following code
causes compile error (void cannot be array element type), I thought
SFINA should match test(...) version instead and not issue any error.

If I replace U[1] with U(*)[1], then the code compiles again. I couldn't
make the sense out of it. What's the magic with (*)? Note that
function<int>::yes returns correct result 0 even with U[1].


What compiler are you using?

as long as U is a function type, U[1] is illformed, array of function
is not allowed.

when you say "U[1]" as a function parameter type, it's deprecated as
"U*"
when you say "U(*)[1]", it's a pointer to array of U.
Please help me out.

Fei

#include <iostream>
using namespace std;

template <typename T>
struct function{

     template <typename U> static char test(...);
     template <typename U> static char (&test(U[1]))[2];

     enum { yes = (sizeof(function<T>::test<T>(0)) == 1) };

};

could you explain your algorithm here?
int main(){

     cout << "int: " <<              function<int>::yes << endl;
     cout << "void: " <<             function<void>::yes << endl;
     // function
     cout << "void(): " <<           function<void()>::yes << endl;
     // function ptr
     cout << "void(*)(): " <<        function<void(*)()>::yes << endl;

}

a clean implementation of is_function from this NG a while ago:

by means of "function to pointer-to-function conversion"


typedef char (&yes) [1];
typedef char (&no) [2];

// is_class_type
template <typename T>
class is_class_type
{
template <typename S>
static yes check(int S::*);

template <typename S>
static no check(...);

public:
static bool const value = (sizeof(check<T>(0)) == sizeof(yes));
};

template <typename T>
class is_function
{
template <typename S>
static yes check(S*);

template <typename S>
static no check(...);

public:
static bool const value =
(!is_class_type<T>::value)
&&
(sizeof(check<T>(*(T*)(0))) == sizeof(yes));
};


__
Best Regards
Barry
 
C

courpron

a clean implementation of is_function from this NG a while ago:

by means of "function to pointer-to-function conversion"

typedef char (&yes) [1];
typedef char (&no)  [2];

// is_class_type
template <typename T>
class is_class_type
{
    template <typename S>
    static yes check(int S::*);

    template <typename S>
    static no check(...);

public:
    static bool const value = (sizeof(check<T>(0)) == sizeof(yes));

};

template <typename T>
class is_function
{
    template <typename S>
    static yes check(S*);

    template <typename S>
    static no check(...);

public:
    static bool const value =
        (!is_class_type<T>::value)
        &&
        (sizeof(check<T>(*(T*)(0))) == sizeof(yes));

};

I think the following should do the same thing but in a shorter way :

template < class T >
struct is_function {
enum { value = is_convertible<void(*)(T),void(*)(T*)>::value };
};

[------

Full working version without an "is_convertible" facility :

typedef char (&yes) [1];
typedef char (&no) [2];

template < class T >
struct is_function {
static yes test ( void(*)(T*) );
static no test(...);
enum { value = sizeof(yes) == sizeof(test( *(void(*)(T)) 0 ) )} ;
};

------]

I didn't test this extensively though.

The rule I'm using from the Standard is :
8.3.5.3 : [...] The type of a function is determined using the
following rules. The type of each parameter 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.
[...]


Alexandre Courpron.
 
B

Barry

a clean implementation of is_function from this NG a while ago:
by means of "function to pointer-to-function conversion"
typedef char (&yes) [1];
typedef char (&no)  [2];
// is_class_type
template <typename T>
class is_class_type
{
    template <typename S>
    static yes check(int S::*);
    template <typename S>
    static no check(...);
public:
    static bool const value = (sizeof(check<T>(0)) == sizeof(yes));

template <typename T>
class is_function
{
    template <typename S>
    static yes check(S*);
    template <typename S>
    static no check(...);
public:
    static bool const value =
        (!is_class_type<T>::value)
        &&
        (sizeof(check<T>(*(T*)(0))) == sizeof(yes));

I think the following should do the same thing but in a shorter way :

template < class T >
struct is_function {
    enum { value = is_convertible<void(*)(T),void(*)(T*)>::value };

};

[------

Full working version without an "is_convertible" facility :

typedef char (&yes) [1];
typedef char (&no)  [2];

template < class T >
struct is_function {
    static yes test ( void(*)(T*) );
    static no test(...);
    enum { value = sizeof(yes) == sizeof(test( *(void(*)(T)) 0 ) )} ;

};

------]

I didn't test this extensively though.

The rule I'm using from the Standard is :
8.3.5.3 : [...] The type of a function is determined using the
following rules. The type of each parameter 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.
[...]

Very impressive!

Well, the implementation I mentioned and the one you did, both have a
defect:
T can't be reference type, as "pointer to reference is not allowed"

so a specialization of is_function is needed for reference type

template <typename T>
struct is_function<T&> {
enum { value = false };
};
 
F

Fei Liu

a clean implementation of is_function from this NG a while ago:
by means of "function to pointer-to-function conversion"
typedef char (&yes) [1];
typedef char (&no) [2];
// is_class_type
template <typename T>
class is_class_type
{
template <typename S>
static yes check(int S::*);
template <typename S>
static no check(...);
public:
static bool const value = (sizeof(check<T>(0)) == sizeof(yes));
};
template <typename T>
class is_function
{
template <typename S>
static yes check(S*);
template <typename S>
static no check(...);
public:
static bool const value =
(!is_class_type<T>::value)
&&
(sizeof(check<T>(*(T*)(0))) == sizeof(yes));
};
I think the following should do the same thing but in a shorter way :
template < class T >
struct is_function {
enum { value = is_convertible<void(*)(T),void(*)(T*)>::value };

Full working version without an "is_convertible" facility :
typedef char (&yes) [1];
typedef char (&no) [2];
template < class T >
struct is_function {
static yes test ( void(*)(T*) );
static no test(...);
enum { value = sizeof(yes) == sizeof(test( *(void(*)(T)) 0 ) )} ;

I didn't test this extensively though.
The rule I'm using from the Standard is :
8.3.5.3 : [...] The type of a function is determined using the
following rules. The type of each parameter 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.
[...]

Very impressive!

Well, the implementation I mentioned and the one you did, both have a
defect:
T can't be reference type, as "pointer to reference is not allowed"

so a specialization of is_function is needed for reference type

template <typename T>
struct is_function<T&> {
enum { value = false };

};

Here is the error message with comeau:
Comeau C/C++ 4.3.10.1 (May 7 2008 20:26:48) for
ONLINE_EVALUATION_BETA1
Copyright 1988-2008 Comeau Computing. All rights reserved.
BD Software STL Message Decryptor (Release 1.24 for Comeau C++)
MODE:strict errors C++ noC++0x_extensions

ComeauTest.c(8): error: array of void is not allowed
template <typename U> static char (&test(U[1]))[2];
^
detected during:
instantiation of "function<void>::test" based on template argument
<void>
at line 10
instantiation of class "function<void>" at line 16

ComeauTest.c(8): error: array of functions is not allowed
template <typename U> static char (&test(U[1]))[2];
^
detected during:
instantiation of "function<void ()>::test" based on template
argument <void
()> at line 10
instantiation of class "function<void ()>" at line 18

2 errors detected in the compilation of "ComeauTest.c".

I have no problem implementing the requirement. What I found strange
here is that SFINAE does not seem to work. Look at the error message,
it's an instantiation error, by definition, SFINAE should kick in and
the ... version should be used. That's what I don't get. I am not
quite sure if 8.3.5.3 is relevant here: array of T decays to pointer
to T. void * or void (*)() would have been ok. Why does not it cause
error with void (*)[1]? It's a pointer to an array of size 1 of void
type. Not fundamentally different from void[1], both are invalid
types.

Fei
 
B

Barry

Fei said:
a clean implementation of is_function from this NG a while ago:
by means of "function to pointer-to-function conversion"
typedef char (&yes) [1];
typedef char (&no) [2];
// is_class_type
template <typename T>
class is_class_type
{
template <typename S>
static yes check(int S::*);
template <typename S>
static no check(...);
public:
static bool const value = (sizeof(check<T>(0)) == sizeof(yes));
};
template <typename T>
class is_function
{
template <typename S>
static yes check(S*);
template <typename S>
static no check(...);
public:
static bool const value =
(!is_class_type<T>::value)
&&
(sizeof(check<T>(*(T*)(0))) == sizeof(yes));
};
I think the following should do the same thing but in a shorter way :
template < class T >
struct is_function {
enum { value = is_convertible<void(*)(T),void(*)(T*)>::value };
};
[------
Full working version without an "is_convertible" facility :
typedef char (&yes) [1];
typedef char (&no) [2];
template < class T >
struct is_function {
static yes test ( void(*)(T*) );
static no test(...);
enum { value = sizeof(yes) == sizeof(test( *(void(*)(T)) 0 ) )} ;
};
------]
I didn't test this extensively though.
The rule I'm using from the Standard is :
8.3.5.3 : [...] The type of a function is determined using the
following rules. The type of each parameter 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.
[...]
Very impressive!

Well, the implementation I mentioned and the one you did, both have a
defect:
T can't be reference type, as "pointer to reference is not allowed"

so a specialization of is_function is needed for reference type

template <typename T>
struct is_function<T&> {
enum { value = false };

};

Here is the error message with comeau:
Comeau C/C++ 4.3.10.1 (May 7 2008 20:26:48) for
ONLINE_EVALUATION_BETA1
Copyright 1988-2008 Comeau Computing. All rights reserved.
BD Software STL Message Decryptor (Release 1.24 for Comeau C++)
MODE:strict errors C++ noC++0x_extensions

ComeauTest.c(8): error: array of void is not allowed
template <typename U> static char (&test(U[1]))[2];
^
detected during:
instantiation of "function<void>::test" based on template argument
<void>
at line 10
instantiation of class "function<void>" at line 16

ComeauTest.c(8): error: array of functions is not allowed
template <typename U> static char (&test(U[1]))[2];
^
detected during:
instantiation of "function<void ()>::test" based on template
argument <void
()> at line 10
instantiation of class "function<void ()>" at line 18

2 errors detected in the compilation of "ComeauTest.c".

I have no problem implementing the requirement. What I found strange
here is that SFINAE does not seem to work. Look at the error message,
it's an instantiation error, by definition, SFINAE should kick in and
the ... version should be used. That's what I don't get. I am not
quite sure if 8.3.5.3 is relevant here: array of T decays to pointer
to T. void * or void (*)() would have been ok. Why does not it cause
error with void (*)[1]? It's a pointer to an array of size 1 of void
type. Not fundamentally different from void[1], both are invalid
types.

I quite agree with you.
As the principle of SFINAE is to avoid producing invalid type, I
couldn't find why it doesn't work here
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,836
Latest member
BuyBlissBitesCBD

Latest Threads

Top