4

我有一个名为的类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();
};

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

4

3 回答 3

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) 是非法的。

于 2013-06-07T21:24:20.687 回答
1


我昨天为“ 检查类是否具有给定签名的成员函数的可能重复项” +1ed rapptz并且没有改变主意。

我想这个问题可以解释为“A)如何检查一个类是否具有给定签名的成员函数和B)如何坚持一个类模板参数是一个A)类”。对于 B) 在这种情况下,我会回答static_assert,因为提问者显然对enable_if替代方案不感兴趣。

这是一个解决方案,它使我对“用于测试 func(args) 是否格式正确并且需要返回类型的特征”的回答进行了调整 该解决方案假定当且仅当公共成员确实存在时,即使重载或继承,它也has_f<T>::value应该是正确的.void T::f()Tff

#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 构建)

于 2013-06-08T08:12:15.563 回答
0

这在回答您的问题和为您的问题提供解决方案之间划出了一条细线,但不直接回答您的问题,但我认为您可能会发现这很有帮助。

有关背景,请查看此问题。作者提到他不喜欢 Boost 的解决方案,我也不是特别喜欢那里提出的解决方案。我正在编写一个快速而肮脏的序列化库(想想 python 的元帅),你可以serialize(object, ostream)在其中调用一个对象来序列化它。我意识到我希望这个函数调用四件事之一:

  1. 如果object是普通的旧数据,只需写出大小和原始数据
  2. 如果object是我用自己的成员函数 ( object::serialize) 创建的类,则调用该成员函数
  3. 如果该类型有模板特化,请使用它。
  4. 如果以上都不成立,则抛出编译错误;序列化功能使用不当。

当我编码时,我会尽量避免那些“棘手”或一目了然难以理解的东西。我认为这个解决方案解决了同样的问题,而无需使用必须思考数小时才能理解的代码:

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

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

于 2013-06-07T22:20:12.993 回答