代码之家  ›  专栏  ›  技术社区  ›  Elias

为什么下面的sfinae测试无法检测模板成员函数?

  •  4
  • Elias  · 技术社区  · 14 年前

    使用gcc编译时,从下面的代码中我总是得到错误的结果。我相信这是一个编译器错误,但有人可能知道得更好。

    #include <iostream>
    
    
    template< class T > 
    class has_apply { 
    
      typedef char yes[1];
      typedef char no[2];
    
      template< class U, U u > 
      struct binder {};
    
      template< class U, unsigned n >
      static yes& test( U*,
                            binder< void (U::*) ( const double& ),
                                &U::template apply< n >
                              >* = 0
                      );
    
      template< class U, unsigned n >
      static no& test( ... );
    
    public:
    
      static const bool result =
             ( sizeof( yes ) == sizeof( test< T, 0u >( (T*)(0) ) ) );
    
    }; 
    
    class A {
    public:
        template< unsigned n >
        void apply( const double& );
    
    };
    
    int main()
    {
      std::cout << std::boolalpha << has_apply< A >::result << '\n';
      return( 0 );
    }
    
    4 回复  |  直到 14 年前
        1
  •  1
  •   Edward Strange    14 年前

    我不能声称理解原因,但是我可以通过不使用u*和拉出binder类型的声明来让代码工作:

    template< class T > 
    class has_apply { 
    
    public:
      typedef char yes[1];
      typedef char no[2];
    
      template< class U, U u > 
      struct binder {};
      typedef binder< void (T::*)(const double&), &T::template apply<0u> > b;
    
      template < typename V, unsigned n >
      struct declare
      {
        typedef binder< void (V::*)(const double&), &V::template apply<n> > type;
      };
    
      template< typename U, unsigned n >
      static yes& test( typename declare<U,n>::type * );
    
      template< class U, unsigned n >
      static no& test( ... );
    
    
      static const bool result =
             ( sizeof( yes ) == sizeof( test< T, 0u >( 0 ) ) );
    
    }; 
    

    实际上,您可以通过从函数中删除无符号参数并将0u粘贴在“declare”中的typedef中来简化这一点。

    同样,我不能解释为什么这个中间元函数是必需的,但是它是必需的,并且上面的功能在MSVC++2010中可以工作。

        2
  •  1
  •   Cheers and hth. - Alf    14 年前

    安迪·维尼科夫在《比较语言C++温和派》(comp.lang.c++温和派)中的回答是:“我只为伟大的google foo(他,我作弊了)而骄傲:

    http://groups.google.com/group/comp.lang.c++.moderated/msg/93017cf706e08c9e

        3
  •  0
  •   Cheers and hth. - Alf    14 年前

    就像诺亚一样,我不知道为什么。与诺亚不同的是,我没有找到一个可行的解决方案,但是我调查了我设法使mingw g++4.4.1编译器崩溃的事情(也就是说,一个 内部编译器错误 )这只是不一致地提到 apply 作为模板和非模板:

    #include <iostream>
    
    
    template< class T > 
    class has_apply { 
      template< class U, U u > 
      struct binder {};
    
      template< class U >
      static double test(
        U*,
        binder<
            void (U::*) ( const double& ),
            //&U::template apply< 0 >
            &U::apply
          >* = 0
      );
    
    public:
    
        static binder<
            void (T::*) ( const double& ),
            &T::template apply< 0 >
          >* dummy();
    
        static const bool result = sizeof( test( (T*)(0), dummy() ) );
    };
    
    class A {
    public:
    //    template< unsigned n >
        void apply( const double& );
    
    };
    
    int main()
    {
      std::cout << std::boolalpha << has_apply< A >::result << '\n';
      return( 0 );
    }
    

    对G++的影响:

    C:\test> g++ -std=c++98 y.cpp
    y.cpp: In instantiation of 'has_apply':
    y.cpp:38:   instantiated from here
    y.cpp:24: internal compiler error: in instantiate_type, at cp/class.c:6303
    Please submit a full bug report,
    with preprocessed source if appropriate.
    See  for instructions.
    
    C:\test> _
    

    他他…

    附言:我想把这个贴成“评论”,因为它不是“答案”。

        4
  •  0
  •   Elias    14 年前

    这并不能解释为什么它不起作用。然而,通过网络进行研究,我找到了一些例子,并最终得到了以下代码,这可能比我一直在尝试的更重要。

    我试图检测一个特定的成员函数签名,但是下面的代码超出了这个范围,检测给定的调用是否可能,不管签名是什么。希望这些评论会有所帮助。

    #include <iostream>
    
    template< class T >
    class has_apply {
    
      class yes { char c; };
      class no { yes c[2]; };
    
      struct mixin {
    void apply( void );
      };
    
      // Calling derived::apply is only non-ambiguous if
      // T::apply does not exist, cf. 10.2.2.
      template< class U> struct derived : public U, public mixin {};
    
      // The following template will help on deduction based on this fact.
      // If U is type void (mixin::*) (void) then the template can be
      // instantiated with u = &derived< U >::apply if and only if T::apply
      // does not exist.
      template< class U, U u >
      class binder {};
    
      // Therefore, the following template function is only selected if there
      // is no T::apply:
      template< class U >
      static no  deduce( U, binder< void (mixin::*) (void), &derived< U >::apply >* = 0 );
      // Selected otherwise.
      static yes deduce( ... );
    
      // Provides an T object:
      static T T_obj( void );
    
    public:
    
      static const bool result = ( sizeof( yes ) == sizeof( deduce( T_obj() ) ) );
    
    };
    
    namespace aux {
    
    // Class to represent the void type as a "true" type.
    class void_type {};
    
    // deduce() some lines below will give us the right answer based on
    // the return type of T::apply<>, but if it is void we cannot use a
    // call to T::apply as an argument to deduce. In fact, the only
    // function in c++ that can take such an argument is operator,() with
    // its default behaviour and if an overload is not well formed it
    // falls back to default.
    template< class T >
    T& operator,( const T&, void_type ) {};
    
    // Copies the constness of T into U. This will be required in order
    // to not get false positives when no const member is defined.
    template< class T, class U >
    struct copy_constness {
      typedef U result;
    };
    template< class T, class U >
    struct copy_constness< const T, U > {
      typedef const U result;
    };
    }
    
    template< class T >
    class has_correct_apply{
    
      class yes { char c; };
      class no { yes c[2]; };
    
      // We assume has_apply< T >::result is true so the following class
      // is well declared. It is declared in a way such that a call to
      // derived::apply< n >() is always possible. This will be necessary
      // later.
      struct derived : public T {
    using T::apply; // possible iff has_apply< T >::result == true
    // This template function will be selected if the function call
    // we wish is otherwise invalid.
    template< unsigned n >
    static no apply( ... );
      };
    
      // const_correct_derived will have the same constness than T.
      typedef typename aux::copy_constness< T, derived >::result const_correct_derived;
      // Provides a const correct derived object.
      static const_correct_derived derived_obj( void );
    
      // Only possible call was derived::apply: call is impossible for signature:
      static no  deduce( no );
      // Since te returned value of it will most likely  be
      // ignored in our code (void must be always [almost, see next]
      // ignored anyway), we return yes from this:
      static yes deduce( ... );
      // As we noticed, an overload of operator,() may make an exact match necessary.
      // If we want this we could simply have used "no" instead of "yes" above and:
    //   static no  deduce( aux::void_type );
    
    
    public:
    
      static const bool result = ( sizeof( yes ) == sizeof( deduce(
    ( derived_obj().template apply< 0u >( 0.0 ), aux::void_type() )
      ) ) );
    
      // Note: Inteestingly enough, GCC does not detect an private subclass default
      // constructor and so const_correct_derived() could be used instead of
      // having a function derived_obj(), but I do not know if this behavoiur is
      // standard or not.
    
    };
    
    
    struct C {
      template< unsigned n >
      int apply( double, unsigned m = 10 ) const;
    private:
      C();
    };
    
    struct D {
      template< unsigned n >
      int apply( const double& );
    private:
      D();
    };
    
    struct E : public C {
    };
    
    struct Without{};
    
    #include "mp.h"
    
    int main()
    {
      std::cout << has_apply< E >::result << '\n';
      std::cout << has_correct_apply< const E >::result << '\n';
      std::cout << has_correct_apply< const D >::result << '\n';
      std::cout << has_correct_apply< D >::result << '\n';
    //   E e;
    
      return( 0 );
    }