Почему следующий тест SFINAE не может обнаружить функцию-член шаблона? - PullRequest
4 голосов
/ 09 ноября 2010

компиляция с GCC я всегда получаю false из следующего кода.Я считаю, что это ошибка компилятора, но кто-то может знать лучше.

#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 ]

1 голос
/ 09 ноября 2010

Ответ Энди Веникова в [comp.lang.c ++. Moderated] (я беру на себя ответственность только за отличный google-foo (хе-хе, я обманул)):

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

1 голос
/ 09 ноября 2010

Я не могу утверждать, что понимаю, почему, но я смог заставить ваш код работать, не взяв 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 ) ) );

}; 

Вы можете упроститьэто немного, удалив неподписанный параметр из функции и просто вставив 0u в typedef в пределах 'Declare'.

Опять же, я не могу объяснить, почему эта промежуточная мета-функция необходима, но она была обязательна, и вышеуказанное работаетв MSVC ++ 2010

0 голосов
/ 13 ноября 2010

Это не ответ на вопрос, почему это не работает.Тем не менее, просматривая в Интернете, я нашел несколько примеров и в итоге получил следующий код, который может быть даже более точным, чем то, что я пытался.сигнатура функции-члена, но приведенный ниже код выходит за рамки и определяет, возможен ли данный вызов, независимо от того, что является сигнатурой.Надеюсь, что комментарии будут полезны.

#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 );
}
0 голосов
/ 09 ноября 2010

Как и Ной, я не знаю почему. В отличие от Ноа, я не нашел работоспособного решения, но исследовал, что мне удалось сломать компилятор 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: Я бы хотел опубликовать это как «комментарий», так как это не «ответ».

...