【问题标题】:Distinguishing structs with type traits区分具有类型特征的结构
【发布时间】:2015-05-28 17:53:45
【问题描述】:

有没有办法将具有std::vector<T>struct 与任何其他类型区分开来?

如果T 是一个包含std::vector<T> 的结构,我有一些模板函数应该专门化,所以我正在寻找的是:

template<typename T> method(const T& object)
{
  static_assert(!contains_vector<T>::value, "This method must be specialized");
  // implementation
}

这样

struct Foo {
  uint32_t foo;
  float bar;
};

struct Bar {
  uint16_t foo;
  vector<float> bar;
}

contains_vector<float>::value == false;
contains_vector<Foo>::value == false;
contains_vector<Bar>::value == true;

我试图弄清楚如何通过&lt;type_traits&gt; 区分这种差异。

【问题讨论】:

  • 为什么要调查某个类的实现细节?
  • 听起来像是 XY 问题。你能解释一下为什么你需要区别对待包含vector 的东西吗?
  • 是什么阻止您从xyz 继承bar 并使用is_base_of&lt;xyz,bar&gt;,或任何其他弯路?
  • 我有很多需要序列化的普通结构,它们都只包含基本类型(数字或枚举常量)或其他普通结构或结构/基本类型的向量。我需要编写一个模板特化来管理包含向量的结构(因为我必须序列化包含的元素,并且我必须确保不可能尝试使用没有模板特化的向量来序列化结构。
  • 使用 is_pod 或 is_standard_layout 检测简单类型怎么样?

标签: c++ templates c++11 typetraits


【解决方案1】:

一般情况下,不会。 C++ 没有反射。所以如果你想写一个类型特征“这个任意的泛型结构是否包含一个vector的成员?”,那是不可能的。

但是,如果您控制所有要测试的类型,则可以使用 BOOST_FUSION_ADAPT_STRUCT 检测它们,这会增加反射:

BOOST_FUSION_ADAPT_STRUCT(
    Foo,
    (uint32_t, foo)
    (float, bar)
)

BOOST_FUSION_ADAPT_STRUCT(
    Bar,
    (uint16_t, foo)
    (std::vector<float>, bar)
)

这使得你的结构 FooBar 从一开始就好像它们是 boost::fusion 序列一样。之后,编写类型特征只涉及所有常见的 Boost Fusion 元编程黑客:

template <typename Seq>
struct contains_vector {
    // metafunction class for checking if a type is a std::vector<T,A>
    struct is_vector {
        template <typename T>
        struct apply : std::false_type { };

        template <typename T, typename A>
        struct apply<std::vector<T,A>> : std::true_type { };    
    };

    // think std::find_if()
    using iter = typename boost::fusion::result_of::find_if<Seq, is_vector>::type;
    // think .end()
    using end = typename boost::fusion::result_of::end<Seq>::type;

    // if iter == end, it's not found, so have to flip the sign
    using type = std::integral_constant<bool, !std::is_same<iter, end>::value>;
};

这样:

static_assert(contains_vector<Bar>::type::value, "");    // OK
static_assert(!contains_vector<Foo>::type::value, "");   // OK

请注意,我使用的是find_ifend 而不是any,因为作为元函数的那个​​总是返回bool

【讨论】:

    【解决方案2】:

    您应该考虑使用SFINAE(替换失败不是错误)

    struct Foo {
      uint32_t foo;
      float bar;
    };
    
    struct Bar {
      typedef vector<float> bar_vector;
      uint16_t foo;
      bar_vector bar;
    };
    

    注意在 struct Bar 中添加了 typedef。

    template <typename T>
    struct contains_vector {
        // Types "yes" and "no" are guaranteed to have different sizes,
        // specifically sizeof(yes) == 1 and sizeof(no) == 2.
        typedef char yes[1];
        typedef char no[2];
    
        template <typename C>
        static yes& test(typename C::bar_vector*);
    
        template <typename>
        static no& test(...);
    
        // If the "sizeof" of the result of calling test<T>(nullptr) is equal to sizeof(yes),
        // the first overload worked and T has a nested type named foobar.
        static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
    };
    

    然后您可以确定结构是否包含指示存在向量的 typedef:

    std::cout << contains_vector<int>::value << std::endl;
    std::cout << contains_vector<Bar>::value << std::endl;
    std::cout << contains_vector<Foo>::value << std::endl;
    

    可以在 C++11 中简化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-01
      • 1970-01-01
      • 2020-01-03
      • 1970-01-01
      • 2019-06-13
      • 1970-01-01
      • 2017-06-24
      相关资源
      最近更新 更多