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

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

1 view
Skip to first unread message

Fei Liu

unread,
May 23, 2008, 5:10:21 PM5/23/08
to
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;
}

Barry

unread,
May 23, 2008, 10:16:14 PM5/23/08
to
On May 24, 5:10 am, Fei Liu <fei....@gmail.com> wrote:
> 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

cour...@gmail.com

unread,
May 24, 2008, 10:28:59 AM5/24/08
to

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.

Barry

unread,
May 25, 2008, 10:44:50 AM5/25/08
to

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 };
};

Fei Liu

unread,
May 26, 2008, 2:33:32 PM5/26/08
to

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

Barry

unread,
May 29, 2008, 1:42:58 AM5/29/08
to

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


--
Best Regards
Barry

0 new messages