为什么以下SFINAE测试无法检测模板成员函数?

时间:2010-11-09 17:03:08

标签: c++ gcc sfinae

使用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 个答案:

答案 0 :(得分:1)

我无法理解为什么,但我能够通过不取出U *并通过拉出活页夹类型的声明来使代码工作:

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

}; 

你可以通过从函数中删除unsigned参数并在'declare'中的typedef中粘贴0u来实际简化这一点。

同样,我无法解释为什么这个中间元函数是必要的,但它是必需的,上面的工作在MSVC ++ 2010中

答案 1 :(得分:1)

Andy Venikov在[comp.lang.c ++。moderated]中的回答(我只是因为google-foo(他是他,我作弊)而受到赞扬):

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

答案 2 :(得分:0)

像诺亚一样,我不知道为什么。与Noah不同,我没有找到可行的解决方案,但调查了我设法使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> _

他他......

PS:我很乐意将其发布为“评论”,因为它不是“答案”。

答案 3 :(得分:0)

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

我试图检测特定的成员函数签名,但下面的代码超出了并检测给定的调用是否可行,无论签名是什么。希望评论会有所帮助。

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