如何创建只在其类型具有特定成员函数时才编译的类?

时间:2013-06-07 19:52:15

标签: c++ c++11

我有一个名为has_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 {};

但是我得到了一些神秘的错误。这是我想要使用的课程:

struct A
{
    void f();
};

我该如何正确地做到这一点?感谢。

3 个答案:

答案 0 :(得分:2)

从你的问题的标题我假设你真的不需要一个派生自true_type或false_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 );
};

这适用于f()不接受任何参数的情况。只有当f返回void时才需要std :: add_pointer,因为sizeof(void)是非法的。

答案 1 :(得分:1)

我昨天为拉丁语写了一张拉普拉兹 “可能重复 Check if a class has a member function of a given signature” 并没有改变主意。

我认为这个问题可以解决这个问题 “A)如何检查某个类是否具有给定签名的成员函数 B)如何坚持课堂模板论证是一门课程 根据A)“。到B)在这种情况下,我会回答static_assert,因为 提问者显然对enable_if替代品不感兴趣。

这是一个适应my answer to "traits for testing whether func(args) is well-formed and has required return type"的解决方案 该解决方案假定has_f<T>::value当前且仅为真 如果公共成员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;
}

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

答案 2 :(得分:0)

这在回答您的问题和提供问题的解决方案之间有一个很好的界限,但没有直接回答您的问题,但我认为您可能会觉得这很有帮助。

有关背景信息,请查看this question。作者提到他不喜欢Boost的解决方案,我也不特别喜欢那里提出的解决方案。我正在写一个快速的&amp;脏序列化库(想想python的marshal),你可以在一个对象上调用serialize(object, ostream)来序列化它。我意识到我希望这个函数调用四个方面之一:

  1. 如果object是普通旧数据,只需写出大小和原始数据
  2. 如果object是我用自己的成员函数(object::serialize)创建的类,则调用该成员函数
  3. 如果该类型有模板专门化,请使用它。
  4. 如果以上都不是,则抛出编译错误;序列化功能使用不当。
  5. 当我编码时,我会尽量避免一些“棘手”或难以理解的内容。我认为这个解决方案解决了同样的问题,而不使用必须经过几个小时思考的代码:

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

    如果您可以将您的需求归结为一套简单的规则,并且可以使用稍微不那么通用的解决方案,我认为这种方法效果很好。