【问题标题】:Why cannot the following SFINAE test detect a template member function?为什么以下 SFINAE 测试无法检测到模板成员函数?
【发布时间】:2010-11-09 17:03:08
【问题描述】:

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

【问题讨论】:

  • 有没有机会简化代码? (我很确定您不需要所有 7 个 yes 模板来演示问题。)或者至少添加一些 cmets?
  • 我已经添加了 6 个测试来涵盖所有可能性,但是 Oli Charlesworth 留下的一个应该足以产生“真实”。
  • 是的,我赌了一把,只有这个是相关的。请随时酌情修改!
  • Andy Venikov 的回答可能已经在 [comp.lang.c++.moderated] 中的线程“Re:为什么以下 SFINAE 测试不起作用?”中可用。干杯,

标签: c++ gcc sfinae


【解决方案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 ) ) );

}; 

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

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

【讨论】:

  • +1 适用于 MSVC。我也用 g++ 4.4.1 尝试过,但是 g++ 说“x.cpp: In static member function 'static char (& has_apply::test(typename has_apply::declare:: type*))[1]': x.cpp:21: error: 'yep' 没有在这个范围内声明”。所以,不完整——我不理解编译器的行为。干杯,
  • 哦,哎呀。您必须删除 yes 测试的正文。我会编辑来做到这一点。我用来计算WTF的部分代码。
  • 更新:我添加了 yep 的定义,然后它编译了 MSVC 和 g++。但后来我向apply 添加了额外的arg 以检查false 结果,然后它没有用任何一个编译器编译。 :-( 你有更多的魔法吗?干杯,
  • 我认为 b 的 typedef 是不需要的,对吧?但是,即使代码结果为“true”,尝试将 const double& 更改为 double& 也会出现编译错误,而不是“false”。至少使用 gcc 4.5
【解决方案2】:

Andy Venikov 在 [comp.lang.c++.moderated] 中的回答(我只是将出色的 google-foo 归功于(他他,我作弊)):

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

【讨论】:

  • 这并不能完全回答为什么它不起作用的问题。请注意,那边的人也不知道为什么结果不是我所期望的。
  • @Elias:我没有完全理解安迪的回答(因此,只链接到它),但他暗示了常量表达式上下文。我能找到的最接近的是 C++98 §5.19/4 “指定非 POD 类对象的成员或基类地址的表达式(第 9 条)不是地址常量表达式”。但我不明白为什么成员函数模板应该使A 类非 POD。当我早些时候检查这个时,我记得(经过适当调整)代码与非模板成员很好地工作。好的,这可能不会直接帮助您。又是一个数据点……干杯,
  • 如果您现在查看帖子,您会发现已经认识到 Andy 在他的第一篇帖子中的推理实际上是错误的。在此之前,我宁愿保持沉默,但也不太相信他的回答。不过,他提供的链接很有价值。
【解决方案3】:

像诺亚一样,我不知道为什么。与 Noah 不同的是,我没有找到可行的解决方案,而是调查了我设法使 MingW g++ 4.4.1 编译器崩溃的事情(即Internal Compiler Error)。这只是通过不一致地将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:在“has_apply”的实例化中: y.cpp:38:从这里实例化 y.cpp:24: 内部编译器错误: in instantiate_type, at cp/class.c:6303 请提交完整的错误报告, 如果合适,使用预处理的源。 有关说明,请参阅。 C:\测试> _

嘿嘿……

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

【讨论】:

    【解决方案4】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-26
      • 1970-01-01
      • 1970-01-01
      • 2013-09-05
      • 2011-03-23
      • 1970-01-01
      • 1970-01-01
      • 2020-10-03
      相关资源
      最近更新 更多