如何使用SFINAE来选择最匹配的类型特征
How do I use SFINAE to choose the closest matching type trait?
场景:
我有多种类型可以归类为序列容器
所有序列容器都是数据结构,但并不是每个数据结构都是序列容器。
下面是一个代码示例。本例中涉及的唯一"重要类型"是Array_T。它分为两类:它是一个序列容器,由于所有序列容器都是数据结构,因此它又是一个数据结构。
//A sequence container type
class Array_T{};
//A type trait for that particular sequence container
template <typename T> struct Is_Array { static const bool value = false; };
template <> struct Is_Array<Array_T> { static const bool value = true; };
//A type trait to identify all of the sequence containers
template <typename T> struct Is_A_Sequence_Container { static const bool value = Is_Array<T>::value
/* would probably "or" together more sequence types, but we only have Array_T in this example */;};
//A type trait to identify all of the data structures
template <typename T> struct Is_A_Data_Structure { static const bool value = Is_A_Sequence_Container<T>::value
/* would probably "or" together more data structure types, but we only have sequence containers in this example */;};
请注意,不能对Array_T进行继承;它必须保持声明的方式。
问题:
我想写两个函数。一个函数将处理所有序列容器,另一个函数处理所有数据结构。我不知道序列容器函数是否真的存在,因为这部分代码可能会生成,也可能不会生成。
那么,我如何使用元模板编程,为类型选择最匹配的标识呢?以下是预期行为的两个例子:
案例1:
// ...
//Both functions exist! Call the more specific one.
// ...
function(Array_T{}); // prints "sequence container"
案例2:
// ...
//Only the data structure one exists(not the sequence container one)
// ...
function(Array_T{}); // prints "data structure"
到目前为止我的尝试:
#include <iostream>
#include <type_traits>
//A sequence container type
class Array_T{};
//A type trait for that particular sequence container
template <typename T> struct Is_Array { static const bool value = false; };
template <> struct Is_Array<Array_T> { static const bool value = true; };
//A type trait to identify all of the sequence containers
template <typename T> struct Is_A_Sequence_Container { static const bool value = Is_Array<T>::value
/* would probably "or" together more sequence types, but we only have Array_T in this example */;};
//A type trait to identify all of the data structures
template <typename T> struct Is_A_Data_Structure { static const bool value = Is_A_Sequence_Container<T>::value
/* would probably "or" together more data structure types, but we only have sequence containers in this example */;};
// ↑ all of this code was already shown to you
//NOTE: This function MAY OR MAY NOT actually appear in the source code
//This function handles all sequence types
template<class T, typename std::enable_if<Is_A_Sequence_Container<T>::value,int>::type=0>
void function(T t) {
std::cout << "sequence container" << std::endl;
return;
}
//This function handles all data structures; assuming a more specific function does not exist(*cough* the one above it)
template<class T, typename std::enable_if<Is_A_Data_Structure<T>::value,int>::type=0>
void function(T t) {
std::cout << "data structure" << std::endl;
return;
}
int main(){
function(Array_T{});
}
现在我意识到这不起作用,因为两个enable_ifs的值都是真的
所以我想在数据结构函数上添加第二个enable_if,以检查序列容器函数是否存在。类似这样的东西:
//...
//This function handles all data structures; assuming a more specific function does not exist(*cough* the one above it)
template<class T, typename std::enable_if<Is_A_Data_Structure<T>::value,int>::type=0,
typename std::enable_if</*if the more specific function does not exist*/,int>::type=0>>
void function(T t) {
std::cout << "data structure" << std::endl;
return;
}
int main(){
function(Array_T{});
}
这就是我陷入困境的地方。有没有一种方法可以在不接触Array_T减速的情况下做到这一点,并且不涉及第三个调度功能?
我会使用标记调度:
struct DataStructureTag {};
struct SequenceContainerTag : public DataStructureTag {};
template <typename T> struct DataStructureTagDispatcher
{
typedef typename std::conditional<Is_A_Sequence_Container<T>::value,
SequenceContainerTag,
DataStructureTag>::type type;
};
// NOTE: This function MAY OR MAY NOT actually appear in the source code
// This function handles all sequence types
template<class T>
void function(T&& t, const SequenceContainerTag&) {
std::cout << "sequence container" << std::endl;
return;
}
// This function handles all data structures (not handled my a more specific function)
template<class T>
void function(T&& t, const DataStructureTag&) {
std::cout << "data structure" << std::endl;
return;
}
template <class T>
typename std::enable_if<Is_A_Data_Structure<T>::value, void>::type
function(T&& t)
{
typedef typename DataStructureTagDispatcher<T>::type tag;
function(t, tag());
}
您还可以使用类层次结构来消除重载的歧义
struct R2 {};
struct R1 : R2 {};
//NOTE: This function MAY OR MAY NOT actually appear in the source code
//This function handles all sequence types
template<class T, typename std::enable_if<Is_A_Sequence_Container<T>::value,int>::type=0>
void function(R1, T t) {
std::cout << "sequence container" << std::endl;
return;
}
//This function handles all data structures; assuming a more specific function does not exist(*cough* the one above it)
template<class T, typename std::enable_if<Is_A_Data_Structure<T>::value,int>::type=0>
void function(R2, T t) {
std::cout << "data structure" << std::endl;
return;
}
int main(){
function(R1{}, Array_T{});
}
我适应了https://stackoverflow.com/a/264088/2684539:
// template funcName should exist
#define HAS_TEMPLATED_FUNC(traitsName, funcName, Prototype)
template<typename U>
class traitsName
{
typedef std::uint8_t yes;
typedef std::uint16_t no;
template <typename T, T> struct type_check;
template <typename T = U> static yes &chk(type_check<Prototype, &funcName>*);
template <typename > static no &chk(...);
public:
static bool const value = sizeof(chk<U>(0)) == sizeof(yes);
}
然后
//NOTE: This function MAY OR MAY NOT actually appear in the source code
//This function handles all sequence types
template<class T, typename std::enable_if<Is_A_Sequence_Container<T>::value,int>::type = 0>
void function(T t) {
std::cout << "sequence container" << std::endl;
return;
}
// this assumes that any template 'function' exists
// (Do you have version for `data structure` ?)
// or else create a dummy struct and then
// template <T>
// typename std::enable_if<std::is_same<T, dummy>::value>::type function(dummy) {}
HAS_TEMPLATED_FUNC(isFunctionExist_Specialized, function<T>, void (*)(T));
// This function handles all data structures not already handled
template<class T, typename std::enable_if<Is_A_Data_Structure<T>::value, int>::type = 0,
typename std::enable_if<!isFunctionExist_Specialized<T>::value, int>::type = 0>
void function(T t) {
std::cout << "data structure" << std::endl;
return;
}
// Care, isFunctionExist_Specialized<T>:: value is computed only once,
// so you have to use another
// `HAS_TEMPLATED_FUNC(isFunctionExist, function<T>, void (*)(T));`
// to take into account these new functions.
相关文章:
- 根据C++标准的定义实现"is_similar"类型特征
- C++类型特征,以查看是否可以<uint32_t>对类型"K"的任何变量调用"static_cast(k)"
- C ++类型特征:确保子类实现方法
- 根据类型特征更改函数定义?
- 如何使用类型特征将函数的通用引用参数限制为 r 值引用?
- 在C++中创建新的类型特征
- 如何构造一个类型特征,可以判断一个类型的私有方法是否可以在另一个类型的构造函数中调用?
- std::begin-类型特征中未考虑用户定义的重载
- 在类型特征修改后,无法限定类型
- 使用类型特征的部分类专用化
- 为什么不调用预期的函数?我是否对类型特征的理解不正确?
- 了解类型特征的体系结构
- 为什么使用某些类型特征的模板类型会导致模板扣除失败
- 有了C++类型特征,有没有办法避免铸造
- 哪种类型特征表明该类型是可分配的?(元组,对)
- 一种类型特征,标识哪个类提供通过重载解析选择的函数
- 如何使用类型特征正确推断引用的参数
- 如何通过扩展以下类型特征来删除 decltype(&MyClass::funct) 部分?
- 从标准库类型特征继承
- 为什么类型特征不适用于命名空间范围内的类型?