当模板过载可用时,检查是否存在自定义的函数重载

时间:2017-04-30 21:36:17

标签: c++ templates c++14 template-meta-programming sfinae

我正在设计一个实用程序标头,用于从sf::InputStream中抽取二进制数据。为了便于使用,它包含一个函数名readFromStream,它有很多(模板化和非模板化)重载,用于自动反序列化标准布局类型和类型化合物,如矢量,元组和我的自定义设计grid课程。完整的实施可以在这里找到:https://github.com/JoaoBaptMG/ReboundTheGame/blob/master/MainGame/utility/streamCommons.hpp

所以,我已经定义了一个重载readFromStream,它通过递归再次调用readFromStream来抽出任何类型的向量:

template <typename T, typename std::enable_if<!is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
    size_t size;

    if (!readFromStream(stream, VarLength(size)))
        return false;

    std::vector<T> newVal(size, T());
    for (auto &val : newVal)
        if (!readFromStream(stream, val))
            return false;

    newVal.swap(value);
    return true;
}

我想为标准布局类编写一个优化版本,因为readFromStream 没有重载,所以我们可以利用它们的内存布局并将它们插入单个read电话:

// trait is_optimization_viable is what I'm having trouble to write
template <typename T, typename std::enable_if<is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
    size_t size;

    if (!readFromStream(stream, VarLength(size)))
        return false;

    std::vector<T> newVal(size, T());
    if (stream.read(newVal.data(), size*sizeof(T)) != size*sizeof(T))
        return false;

    newVal.swap(value);
    return true;
}

好吧,我可以使用其他答案中描述的解决方案来检测函数的存在,但是有一个问题。当类型是标准布局时,我有一个默认的readFromStream,其内容如下:

template <typename T, typename std::enable_if<std::is_standard_layout<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, T& value)
{
    return stream.read((void*)&value, sizeof(T)) == sizeof(T);
}

所以,总是有一个函数来完成序列化,而不仅仅是我想要的那个。我想在这里解决的问题是:如何检测类型为readFromString的非默认T是否存在,以便禁用readFromString的优化版本std::vector<T>

我试图拉一些技巧。我无法将优化限制为POD类型,因为我在某些我要反序列化的类型上使用sf::Vector2<T>,这不是POD。我尝试比较我使用非模板化和模板化函数时得到的函数地址,如:

using FPtr = bool(*)(sf::InputStream&, T&);
return (FPtr)readFromStream == (FPtr)readFromStream<T>;

但是,奇怪的是,它没有用。我研究了很多的解决方案,但没有一个我可以适应我需要的东西。也许这在C ++中是不可能的,我将不得不求助于“标记”我不想优化的类型。或许这是一些我没有想到的模糊模板。我怎么能这样做?

1 个答案:

答案 0 :(得分:1)

据我所知,你的问题是:

is_optimization_viable<T>;

可以通过以下方式定义:

template<typename T>
using is_optimization_viable<T> = std::is_standard_layout<T>;

但是对于T的某些值标准布局的事实 尽管如此,您仍需要自定义bool readFromStream(sf::InputStream &stream, T &value), 过载意味着他们优化 - 可行。

因为你必须编写这些自定义重载,你知道那些 T的特殊值。说它们是XYZ类型。 然后您可以将特征定义为:

#include <type_traits>

template<typename T, typename ...Us>
struct is_one_of;

template<typename T>
struct is_one_of<T> {
    static constexpr bool value = false;
};

template<typename T, typename First, typename ...Rest>
struct is_one_of<T,First,Rest...> {
    static constexpr bool value =
        std::is_same<T,First>::value || is_one_of<T,Rest...>::value;
};

// ^ C++17: `std::disjunction` does the job

template<typename T>
using has_custom_read_from_stream = is_one_of<T,X,Y,Z>;

template<typename T>
struct is_optimization_viable {

    static constexpr bool value = std::is_standard_layout<T>::value &&
        !has_custom_read_from_stream<T>::value;
};

我很欣赏你宁愿避免持续维护 硬编码的类型列表XYZ,并且更喜欢SFINAE-probe 是否呼叫readFromStream(s, t)将是对其中一个呼叫 某些std::declval-ed st的自定义重载。

但那是海市蜃楼。你告诉我们,会有一些重载 readFromStream(s, t)将编译t的任何类型。 如果是这样,SFINAE探针将始终告诉您是, readFromStream(s, t) 将编译 - 任何T作为t的非限定类型。你呢 仍然必须做出编译时决定T是否是其中之一 自定义类型,如果没有,是否为标准布局。

这就是问题所在。判断T是否是其中之一 自定义类型,您必须测试它与任何一个身份 如图所示,它们是分离的,或者你必须找到一个独立于它们的特征 所有和唯一的自定义类型都满足的身份。就像你一样 不要告诉我们那些自定义类型是什么,我不能建议任何这样的特性, 但是如果你找到一个将定义或替换has_custom_read_from_stream<T>

顺便说一句,我是第二个@NirFriedman的评论:std::standard_layout真的是你的意思吗?