使用SFINAE检测编译时是否存在过载的独立功能

时间:2017-12-04 16:29:44

标签: c++ templates sfinae c++03

这是我的unsuccessful attempt,用于检测C ++ 98/03中是否存在任何类/结构void set( T& , void (T::*Func)( const char* ) , const char* str )的独立函数T

#include <iostream>
#include <typeinfo>

struct Foo;
struct Bar;
struct Fuu;

void set( Foo& , void (Foo::*Func)( const char* ) , const char* str )
{
}

void set( Bar& , void (Bar::*Func)( const char* ) , const char* str )
{
}

template <typename T>
class setter_is_implemented
{
public: 
    typedef void (*SetterFunction)( T& , void (T::*)( const char* ) , const char* );

    typedef char one;
    typedef long two;

    template <typename C> static one test( C c, SetterFunction = c ) ;
    template <typename C> static two test(...);    

public:
    enum { value = sizeof( test<T>(&set)) == sizeof(one) };
};

int main()
{
    std::cout << setter_is_implemented<Foo>::value << std::endl;
    std::cout << setter_is_implemented<Bar>::value << std::endl;
    std::cout << setter_is_implemented<Fuu>::value << std::endl;
}

GCC错误消息

Test.cpp:24:66: error: ‘c’ was not declared in this scope
     template <typename C> static one test( C c, SetterFunction = c ) ;
                                                                  ^
Test.cpp: In instantiation of ‘class setter_is_implemented<Foo>’:
Test.cpp:33:44:   required from here
Test.cpp:28:35: error: address of overloaded function with no contextual type information
     enum { value = sizeof( test<T>(&set)) == sizeof(one) };
                                   ^
Test.cpp: In instantiation of ‘class setter_is_implemented<Bar>’:
Test.cpp:34:44:   required from here
Test.cpp:28:35: error: address of overloaded function with no contextual type information
Test.cpp: In instantiation of ‘class setter_is_implemented<Fuu>’:
Test.cpp:35:44:   required from here
Test.cpp:28:35: error: address of overloaded function with no contextual type information

1 个答案:

答案 0 :(得分:1)

AFAIK,在重载解析之前发生重载函数的地址,因此SFINAE无法以这种方式工作。这也更难,因为你的函数返回void(否则,只会检查结果的大小)。我的C ++ 03元编程很生疏,但这应该有效( live on coliru ):

namespace setter_is_implemented_detail {
  struct dummy1 { char c[2]; };
  typedef char dummy2;

  dummy2 operator,(dummy1, dummy1);

  template<typename T> dummy1 set(T const&,...);

  template <typename T>
  struct impl
  {
    typedef void (T::*FT)( const char* );
    static T& x;

    enum { value = sizeof( set(x,static_cast<FT>(0),static_cast<const char*>(0)), dummy1() ) != sizeof(dummy2) };
  };
}

template <typename T>
struct setter_is_implemented: setter_is_implemented_detail::impl<T>{};

请注意以下几点:

    ADL在这里需要找到
  • set()(根据您的问题描述,这不是什么大问题)(*)
  • 由于void返回类型,需要逗号运算符技巧(**);在gcc中,你可以获取sizeof(void),因此可以简化代码
  • 这不适用于非对象类型(由于static T&)。如果你需要的话,它可以工作......

(*)请注意,这不适用于每个可能的set()重载;对于任何一个sfinae都是如此,即使在c ++ 17中,set()也必须总是对sfinae友好才能工作。

如果你需要完全匹配sfinae友好的set()重载,它仍然可以做到,但它更复杂;我分两个阶段进行:首先使用上面的代码找到可能的候选者,然后检查函数指针转换,就像在原始代码中一样。

(**)这个技巧有效,因为如果set()具有void返回类型(因此存在),则逗号始终被解释为内置逗号运算符(请参阅 c ++ 03 [3.9.1 / 9]:&#34; void类型的表达式只能用作表达式语句(6.2),作为逗号表达式的操作数[...]&#34; )导致最右边的表达。否则,使用虚拟集和逗号运算符。