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

Discussion in 'C++' started by Fei Liu, May 23, 2008.

  1. Fei Liu

    Fei Liu Guest

    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;
    }
     
    Fei Liu, May 23, 2008
    #1
    1. Advertising

  2. Fei Liu

    Barry Guest

    On May 24, 5:10 am, Fei Liu <> 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
     
    Barry, May 24, 2008
    #2
    1. Advertising

  3. Fei Liu

    Guest

    On 24 mai, 04:16, Barry <> wrote:
    > 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.
     
    , May 24, 2008
    #3
  4. Fei Liu

    Barry Guest

    On May 24, 10:28 pm, wrote:
    > On 24 mai, 04:16, Barry <> wrote:
    >
    >
    >
    > > 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 };
    };
     
    Barry, May 25, 2008
    #4
  5. Fei Liu

    Fei Liu Guest

    On May 25, 10:44 am, Barry <> wrote:
    > On May 24, 10:28 pm, wrote:
    >
    >
    >
    > > On 24 mai, 04:16, Barry <> wrote:

    >
    > > > 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
     
    Fei Liu, May 26, 2008
    #5
  6. Fei Liu

    Barry Guest

    Fei Liu wrote:
    > On May 25, 10:44 am, Barry <> wrote:
    >> On May 24, 10:28 pm, wrote:
    >>
    >>
    >>
    >>> On 24 mai, 04:16, Barry <> wrote:
    >>>> 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


    --
    Best Regards
    Barry
     
    Barry, May 29, 2008
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Emmanuel Deloget
    Replies:
    2
    Views:
    488
    Emmanuel Deloget
    Nov 29, 2006
  2. siddhu
    Replies:
    1
    Views:
    314
    Victor Bazarov
    May 17, 2007
  3. Replies:
    2
    Views:
    511
    Kai-Uwe Bux
    Oct 23, 2007
  4. Barry
    Replies:
    2
    Views:
    375
    Fei Liu
    May 29, 2008
  5. Victor Bogado
    Replies:
    3
    Views:
    512
    James Kanze
    Mar 3, 2011
Loading...

Share This Page