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

发布于 2024-10-01 13:32:58 字数 920 浏览 8 评论 0原文

使用 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 );
}

compiling with GCC i get always false from the following code. I believe this is a compiler bug, but someone may know better.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

云醉月微眠 2024-10-08 13:32:58

我不能声称理解为什么,但我能够通过不采用 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 ) ) );

}; 

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

再说一遍,我无法解释为什么这个中间元函数是必要的,但它是必需的,并且上述内容在 MSVC++ 2010 中有效

I can't claim to understand why, but I was able to make your code work by not taking U* and by pulling the declaration of the binder type out:

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

}; 

You can actually simplify this a bit by removing the unsigned parameter from the function and just sticking 0u in the typedef within 'declare'.

Again, I can't explain why this intermediate metafunction is necessary but it was required and the above works in MSVC++ 2010

最终幸福 2024-10-08 13:32:58

Andy Venikov 在 [comp.lang.c++.moderated] 中的回答(我只是将功劳归功于伟大的 google-foo(呵呵,我作弊了)):

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

Andy Venikov's answer over in [comp.lang.c++.moderated] (I'm only taking credit for great google-foo (he he, I cheated)):

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

栀梦 2024-10-08 13:32:58

就像诺亚一样,我不知道为什么。与 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:我很乐意将此作为“评论”发布,因为它不是一个“回答”。

Like Noah I don't know why. Unlike Noah I didn't find a workable solution, but investigating the thing I managed to crash the MingW g++ 4.4.1 compiler (that is, an Internal Compiler Error). This was simply by inconsistently referring to apply as template and non-template:

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

Effect on 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> _

He he...

PS: I'd love to post this as a "comment", since it's not an "answer".

眼睛会笑 2024-10-08 13:32:58

这并不是它不起作用的原因的答案。然而,通过网络研究,我找到了一些示例,并最终得到了以下代码,这可能比我一直在尝试的更切题。

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

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

This is not an answer to why it doesn't work. However, researching through the web, I've found some examples and eventually got to the following code, which may be even more to the point then what I've been trying.

I was trying to detect an specific member function signature, but the code below goes beyond and detects whether a given call is possible, no matter what is the signature. Hope the comments will be helpful.

#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 );
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文