【问题标题】:Using SFINAE to detect if overloaded free-standing function exists at compile-time使用 SFINAE 检测编译时是否存在重载的独立函数
【发布时间】:2017-12-04 16:29:44
【问题描述】:

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

#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

【问题讨论】:

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


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

    注意几点:

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

    (*) 请注意,这不适用于所有可能的set() 过载;对于任何 sfinae 都是如此,即使在 c++17 中,set() 必须始终对 sfinae 友好才能工作。

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

    (**) 这个技巧很有效,因为如果set() 具有void 返回类型(因此存在),逗号总是被解释为内置的逗号运算符(参见 c++03[3.9. 1/9]:“void 类型的表达式只能用作表达式语句 (6.2),作为逗号表达式的操作数 [...]”) 导致最右边的表达式。否则,使用哑集和逗号运算符。

    【讨论】:

    • 简直太棒了!!! ....只是几个问题。为什么sizeof( dummy1(), set(x,0,0), dummy1() ) 而不仅仅是sizeof(set(x,0,0), dummy1() )
    • ...我也不太明白operator, 是如何绕过sizeof(void)
    • @Olumide,是的 sizeof 可以简化,这是以前尝试的复制过去的残余,已编辑
    • @olumide,关于'operator',如果设置'returns' void,逗号总是被解释为内置逗号,导致最后一个表达式被计算(=dummy1() );如果未找到 set,则选择虚拟集以及重载的逗号运算符,从而导致 dummy2...
    • "如果设置'returns' void,逗号总是被解释为内置逗号"你能提供一个参考吗?
    猜你喜欢
    • 2017-05-01
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    • 2014-07-22
    • 1970-01-01
    相关资源
    最近更新 更多