【问题标题】:Extensible type traits in C++C++ 中的可扩展类型特征
【发布时间】:2016-09-13 09:16:01
【问题描述】:

我想编写一个通用序列化库,它提供例如一个通用的save 函数。 该库包含自定义类型特征,例如some_condition:

template <typename T>
struct some_condition
{
    constexpr static bool value = std::is_same<std::string, T>::value ||std::is_arithmetic<T>::value ;
};

save的行为是根据some_condition选择的:

template <typename T>
std::enable_if_t<some_condition<T>::value> save(const T& value)
{
    std::cout << "these types will be handled in a specific way: " << value << std::endl;
}

template <typename T>
std::enable_if_t<!some_condition<T>::value> save(const T& value)
{
    std::cout << "these types will be handled in another way: " << value << std::endl;
}

save 应该可以为用户数据类型定制,不仅通过重载,而且通常通过特征。 因此我创建了trait_extension,它可以专门用于特征模板:

template <template<typename> class Trait, typename T>
struct trait_extension : Trait<T>
{
}

save 必须相应修改:

template <typename T>
std::enable_if_t<trait_extension<some_condition,T>::value> save(const T& value) { ... }


template <typename T>
std::enable_if_t<!trait_extension<some_condition,T>::value> save(const T& value) { ... }

用户现在可以提供自己的专业化trait_extension

template <typename T>
struct trait_extension<some_condition, T>
{  
    // user specific extension: exclude floats from condition
    constexpr static bool value = !std::is_floating_point<T>::value && some_condition<T>::value;
};

我的问题::

有没有“更好”/更优雅的方式来实现可扩展的特征?

live example

【问题讨论】:

  • 如果有一个“一般情况”,其中 is_string 被定义为 false,并且专门定义任何被认为是字符串的东西,难道不是更简单吗?
  • 你看过C++ Container Pretty-Printer作为参考吗?
  • "但是,这只有在 trait_extension 特化存在于 print 之前才有效。"嗯?
  • @ildjarn print 与实际功能不同,这只是一个示例;我正在寻找如何实现可扩展特征的建议,我在 cxx-prettyprint 中没有找到任何参考
  • @T.C.奇怪,当我昨天尝试时,trait_extension 的位置(print 的上方/下方)似乎很重要……再试一次,now it works;不过,关于如何实现可扩展性功能,还有什么“更好”/更优雅的方法吗?

标签: c++ templates c++14 typetraits


【解决方案1】:

我认为你的方法一点也不优雅。它很容易变成意大利面条代码,并且难以维护或使用。我会改为采用基于“策略”的方法,类似于标准库中的std::allocator 类。更改行为是实现分配器接口并将其作为模板参数提供的简单问题。然后一切都会自动进行。

一方面,在“通用序列化”库中,您不仅需要担心类型,还需要担心本地化。它可以像使用, 而不是. 一样简单,也可以像统一大写一样复杂。使用您的方法,修改流或语言环境(即 std vs boost)后端并不是很容易,但使用基于策略的方法,这是一个搜索和替换的问题:

serialize<int, std_locale<int>>(32.000)
serialize<int, boost_locale<int>>(32.000)

这允许您在主语言环境类 ala std::allocator 中提供一组“默认值”,然后用户可以从中继承并更改一种或两种类型的行为,而不是提供疯狂的 SFINAE 重载。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-04
    • 1970-01-01
    • 2021-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    相关资源
    最近更新 更多