C++ 如何创建一个仅在其类型具有特定成员函数时才编译的类?

C++ 如何创建一个仅在其类型具有特定成员函数时才编译的类?,c++,c++11,C++,C++11,我有一个名为has\u f的类,我希望它只接受具有f成员函数的模板参数。我该怎么做?这就是我所尝试的: template <typename T, typename = void> struct has_f : std::false_type {}; template <typename T> struct has_f< T, typename = typename std::enable_if< typename T::f

我有一个名为
has\u f
的类,我希望它只接受具有
f
成员函数的模板参数。我该怎么做?这就是我所尝试的:

template <typename T, typename = void>
struct has_f : std::false_type {};

template <typename T>
struct has_f<
    T,
     typename = typename std::enable_if<
        typename T::f
    >::type
> : std::true_type {};

如何正确地执行此操作?谢谢。

根据您问题的标题,我假定您并不真正需要从true\u type或false\u type派生的类型-仅在方法f不存在时防止编译。如果是这种情况,并且如果您还需要该方法的特定签名(至少在参数方面),在C++11中,您可以执行以下操作:

template <typename T>
struct compile_if_has_f
{
    static const size_t dummy = sizeof(
        std::add_pointer< decltype(((T*)nullptr)->f()) >::type );
};
模板
结构编译\u如果\u有\u f
{
静态常数size\u t dummy=sizeof(
std::add_指针f())>:type);
};

这适用于f()不应接受任何参数的情况。std::add_指针仅在f返回void时才需要,因为sizeof(void)是非法的。

这在回答您的问题和提供问题的解决方案(但不直接回答您的问题)之间划出了一条细线,但我想您可能会觉得这很有帮助

有关背景信息,请查看。作者提到他不喜欢Boost的解决方案,我也不特别喜欢那里提出的方案。我正在编写一个快速而肮脏的序列化库(想想python的封送),在这里您可以调用对象上的
serialize(object,ostream)
来序列化它。我意识到我想要对四件事中的一件进行函数调用:

  • 如果
    对象
    是普通的旧数据,只需写出大小和原始数据即可
  • 如果
    object
    是我用自己的成员函数(
    object::serialize
    )创建的类,则调用该成员函数
  • 如果该类型有模板专门化,请使用它
  • 如果以上都不正确,抛出编译错误;序列化函数使用不正确
  • 当我编写代码时,我会尽量避免那些“棘手”或一眼就看不懂的东西。我认为这个解决方案解决了同样的问题,而不需要使用必须思考几个小时才能理解的代码:

    #include <type_traits>
    #include <iostream>
    #include <vector>
    #include <string>
    
    // Template specialization for a POD object
    template<typename T> 
    typename std::enable_if< std::is_pod<T>::value, bool>::type
    serial(const T &out, std::ostream &os)
    {
        os.write((const char*) &out, sizeof(T));
        return os.good();
    }
    
    // Non POD objects must have a member function 'serialize(std::ostream)'
    template<typename T> 
    typename std::enable_if< ! std::is_pod<T>::value, bool>::type
    serial(const T &out, std::ostream &os)
    {
        return out.serial(os);
    }
    
    // Additional specializations here for common container objects
    template<typename T> 
    bool serial(const std::vector<T> &out, std::ostream &os)
    {
        const size_t vec_size = out.size();
    
        if(!serial(vec_size, os))
            return false;
    
        for(size_t i =0; i < out.size(); ++i)
        {
            if(!serial(out[i], os))
                return false;
        }
    
        return true;
    }
    
    class SomeClass
    {
        int something;
        std::vector<double> some_numbers;
    
        ...
    
        bool serial(std::ostream &os)
        {
            return serial(something, os) && serial(some_numbers, os);
        }
    };
    
    #包括
    #包括
    #包括
    #包括
    //POD对象的模板专门化
    模板
    typename std::enable_if::type
    串行(常数T&out,标准::ostream&os)
    {
    操作系统写入((const char*)&out,sizeof(T));
    返回os.good();
    }
    //非POD对象必须具有成员函数“serialize(std::ostream)”
    模板
    typename标准::如果<,则启用!std::is_pod::value,bool>::type
    串行(常数T&out,标准::ostream&os)
    {
    返回。串行(os);
    }
    //此处对常见容器对象进行了其他专门化
    模板
    bool串行(常量std::vector&out,std::ostream&os)
    {
    const size_t vec_size=out.size();
    如果(!串行(向量大小,操作系统))
    返回false;
    对于(size_t i=0;i

    如果你能将你的需求归结为一套简单的规则,并能接受一个稍微不那么通用的解决方案,我认为这种方法很有效。

    I+1ed rapptz forday for
    “可能的副本 " 我也没有改变主意

    我想这个问题对我来说是有争议的 “A)如何检查类是否具有给定签名的成员函数,以及 B) 如何坚持类模板参数是类 根据A)“。对于B)在这种情况下,我将用
    静态断言回答,因为
    提问者显然对
    enable\u if
    备选方案不感兴趣

    这里有一个适合的解决方案 此解决方案假定
    has_f::value
    应为true,当且仅当 如果确实存在公共成员
    void T::f()
    ,即使
    T
    重载
    f
    或继承
    f

    #include <type_traits>
    
    template<typename T>
    struct has_f
    {   
        template<typename A>
        static constexpr bool test(
            decltype(std::declval<A>().f()) *prt) {
            return std::is_same<void *,decltype(prt)>::value;
        }
    
        template <typename A>
        static constexpr bool test(...) {
            return false; 
        }
    
        static const bool value = test<T>(static_cast<void *>(nullptr)); 
    };
    
    // Testing...
    
    struct i_have_f
    {
        void f();   
    };
    struct i_dont_have_f
    {
        void f(int);    
    };
    struct i_also_dont_have_f
    {
        int f();    
    };
    struct i_dont_quite_have_f
    {
        int f() const;  
    };
    struct i_certainly_dont_have_f
    {};
    
    struct i_have_overloaded_f
    {
        void f();
        void f(int);
    };
    struct i_have_inherited_f : i_have_f
    {};
    
    #include <iostream>
    
    
    template<typename T>
    struct must_have_f{
        static_assert(has_f<T>::value,"T doesn't have f");
    };
    
    int main()
    {
        must_have_f<i_have_f> t0; (void)t0;
        must_have_f<i_have_overloaded_f> t1; (void)t1;
        must_have_f<i_have_inherited_f> t2; (void)t2;
        must_have_f<i_dont_have_f> t3; (void)t3; // static_assert fails
        must_have_f<i_also_dont_have_f> t4; (void)t4; // static_assert fails
        must_have_f<i_dont_quite_have_f> t5; (void)t5; // static_assert fails
        must_have_f<i_certainly_dont_have_f> t6; (void)t6; // static_assert fails
        must_have_f<int> t7; (void)t7; // static_assert fails
        return 0;
    }
    
    #包括
    模板
    结构有
    {   
    模板
    静态constexpr布尔测试(
    decltype(std::declval().f())*prt){
    返回std::is_same::value;
    }
    模板
    静态constexpr布尔测试(…){
    返回false;
    }
    静态常量布尔值=测试(静态_转换(nullptr));
    };
    //测试。。。
    结构i__f
    {
    无效f();
    };
    结构我没有
    {
    无效f(int);
    };
    结构i\u也\u没有
    {
    int f();
    };
    结构我不太有
    {
    int f()常数;
    };
    结构我当然没有
    {};
    结构i\u已重载\u f
    {
    无效f();
    无效f(int);
    };
    结构i_已继承:i_已
    {};
    #包括
    模板
    结构必须具有{
    静态断言(有f::值,“T没有f”);
    };
    int main()
    {
    必须具有t0;(无效)t0;
    必须有(无效)t1;
    必须有t2;(无效)t2;
    必须\u有\u f t3;(void)t3;//静态\u断言失败
    必须\u有\u f t4;(void)t4;//静态\u断言失败
    必须\u有\u f t5;(void)t5;//静态\u断言失败
    必须\u有\u f t6;(void)t6;//静态\u断言失败
    必须\u有\u f t7;(void)t7;//静态\u断言失败
    返回0;
    }
    

    (使用clang 3.2、gcc 4.7.2/4.8.1构建)

    如果
    f
    过载怎么办?中的开始/结束特征检查有一些检查成员的示例。是否要检测模板?重载函数?继承的函数?@johanneschaub litb No,
    f
    不带参数,是类中的常规
    void
    函数。1)可能的重复项需要
    typename std::add_pointer…
    进行编译。2) 我发现GCC4.7.2/4.8.1(与Clang3.2不同)不会在
    dummy
    上呕吐,除非对其进行评估。需要
    静态断言
    。3) 也允许
    f
    @MikeKinghan的任何返回类型,感谢您的更正。我在VisualStudio2010中测试了这段代码,这就是为什么我忘记了
    typename
    (VS在某些情况下不需要它)
    #include <type_traits>
    
    template<typename T>
    struct has_f
    {   
        template<typename A>
        static constexpr bool test(
            decltype(std::declval<A>().f()) *prt) {
            return std::is_same<void *,decltype(prt)>::value;
        }
    
        template <typename A>
        static constexpr bool test(...) {
            return false; 
        }
    
        static const bool value = test<T>(static_cast<void *>(nullptr)); 
    };
    
    // Testing...
    
    struct i_have_f
    {
        void f();   
    };
    struct i_dont_have_f
    {
        void f(int);    
    };
    struct i_also_dont_have_f
    {
        int f();    
    };
    struct i_dont_quite_have_f
    {
        int f() const;  
    };
    struct i_certainly_dont_have_f
    {};
    
    struct i_have_overloaded_f
    {
        void f();
        void f(int);
    };
    struct i_have_inherited_f : i_have_f
    {};
    
    #include <iostream>
    
    
    template<typename T>
    struct must_have_f{
        static_assert(has_f<T>::value,"T doesn't have f");
    };
    
    int main()
    {
        must_have_f<i_have_f> t0; (void)t0;
        must_have_f<i_have_overloaded_f> t1; (void)t1;
        must_have_f<i_have_inherited_f> t2; (void)t2;
        must_have_f<i_dont_have_f> t3; (void)t3; // static_assert fails
        must_have_f<i_also_dont_have_f> t4; (void)t4; // static_assert fails
        must_have_f<i_dont_quite_have_f> t5; (void)t5; // static_assert fails
        must_have_f<i_certainly_dont_have_f> t6; (void)t6; // static_assert fails
        must_have_f<int> t7; (void)t7; // static_assert fails
        return 0;
    }