当模板重载可用时,检查是否存在函数的自定义重载
Check presence of custom overload of function when template overload is available
我正在设计一个实用程序标头,可以从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);
}
因此,总有一个函数可以执行序列化,而不仅仅是我想要的函数。我想在这里解决的问题是:如何检测类型T
是否存在非默认readFromString
,以便禁用std::vector<T>
的优化版本的readFromString
?
我试图拉一些技巧。我不能将优化限制为 POD 类型,因为我正在对要反序列化的某些类型(不是 POD)使用sf::Vector2<T>
。我尝试比较使用非模板化和模板化函数时获得的函数地址,例如:
using FPtr = bool(*)(sf::InputStream&, T&);
return (FPtr)readFromStream == (FPtr)readFromStream<T>;
但是,奇怪的是,它没有奏效。我研究了很多解决方案,但没有一个我能适应我需要的。也许这在C++是不可能的,我将不得不重新"标记"我不想优化的类型。或者也许是我没有想到的一些晦涩难懂的模板。我该怎么做?
据我了解,您的问题是:
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
的特殊价值是。假设它们是X
型、Y
型、Z
型。 然后,您可以将特征定义为:
#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;
};
我很感激你宁愿避免持续维护 硬编码的字体列表X
、Y
、Z
,并且更喜欢SFINAE探针 呼叫readFromStream(s, t)
是否为对其中一个 某些std::declval
-eds
和t
的自定义重载。
但那是海市蜃楼。你告诉我们,会有一些过载readFromStream(s, t)
将编译任何类型的t
. 如果是这样,SFINAE 探头将始终告诉您是的,readFromStream(s, t)
将编译- 对于任何T
作为非限定类型的t
.还有你 仍然必须做出编译时决定T
是否是 自定义类型,如果不是,则是否为标准布局。
这就是问题的全部内容。判断T
是否是 自定义类型,您必须测试它与任何一个的标识 如图所示,它们是分离的,或者你必须找到一个独立于他们的特征 所有且仅由自定义类型满足的标识。当你 不要告诉我们这些自定义类型是什么,我不能建议任何这样的特征, 但是如果你找到一个,那么它将定义或替换has_custom_read_from_stream<T>
。
顺便说一下,我赞同@NirFriedman的评论:std::standard_layout
真的是你的意思吗?
- 从模板检查成员函数重载是否存在
- 函数重载,仅在存在替代函数时才应用enable_if
- C++基于模板成员的存在进行成员重载
- 如何在C++编译时检查运算符的特定重载是否存在
- 当模板重载可用时,检查是否存在函数的自定义重载
- 为什么转换运算符在存在 const-ref 和值时会导致不明确的重载
- 运算符重载:简单添加...错误 C2677:二进制"+":未找到采用类型 ___ 的全局运算符(或者不存在可接受的转换)
- 在存在新的初始值设定项序列的情况下,运算符重载解析如何工作
- 为什么 std::initializer_list 等人不存在 std::make_unique 重载?
- 如果我们在c++中重载构造函数,那么默认构造函数是否仍然存在
- 为什么存在 C++11 std::initializer_list 构造函数重载规则
- SFINAE:当重载移动到其他命名空间时,检查函数是否存在会中断
- 如果存在可以匹配另一个重载函数的函数模板,将调用哪个函数
- 编写具有枚举基但仅具有clang的枚举时存在不明确的重载
- 如果存在具有不同参数类型的重载,则在全局命名空间中找不到函数
- 运算符重载实现:0xC0000005:读取位置存在访问冲突
- 基于依赖类型存在的重载
- 当派生类中存在另一个重载时,无法调用基类的方法
- 外部"C"、重载和函数指针可能存在歧义
- 每个构造函数、方法和运算符重载都存在c++多定义错误