我正在尝试将 c 风格的变体包装在库中。它使用类型
enum
,我的意思是使用模板来填充。
简化版本大致如下:
enum Type
{
Type_Int,
Type_Double,
Type_String
};
template<typename T>
Type typeId();
template<> Type typeId<int>() { return Type_Int; }
template<> Type typeId<double>(){ return Type_Double; }
template<> Type typeId<const char*>() { return Type_String; }
template<> Type typeId<char[10]>() { return Type_String; }
// not allowed
// template<int N> Type typeId<char[N]>() { return Type_String; }
};
https://godbolt.org/z/3GsKv6PEn
我可以很好地识别
char[10]
,但我想对char[N]
做一个一般情况,这是不允许的。那么我该如何识别所有 char[N]
类型呢?
ps。它是嵌入式的,所以我不喜欢尽可能使用
std::string
。
你不能部分特化一个函数。但你可以部分专门化类或结构。
因此,要解决此问题,请在类中执行模板逻辑,然后在函数中使用它:
enum Type {
Type_Int,
Type_Double,
Type_String,
};
namespace detail {
template <typename T>
class TypeId;
template <>
class TypeId<int> {
public:
static constexpr Type value = Type_Int;
};
template <>
class TypeId<double> {
public:
static constexpr Type value = Type_Double;
};
template <>
class TypeId<const char*> {
public:
static constexpr Type value = Type_String;
};
template <int N>
class TypeId<const char[N]> {
public:
static constexpr Type value = Type_String;
};
}
template <typename T>
constexpr Type typeId()
{
return detail::TypeId<T>::value;
}
static_assert(typeId<int>() == Type_Int);
static_assert(typeId<double>() == Type_Double);
static_assert(typeId<const char*>() == Type_String);
static_assert(typeId<const char[2]>() == Type_String);
static_assert(typeId<const char[10]>() == Type_String);
https://godbolt.org/z/4zfb47Mvx
为了使其更能抵抗 cv 限定符类型变化,您可以使用 std::remove_cvref_t :https://godbolt.org/z/esGf4Wc8h
我该如何识别所有
类型?char[N]
班级专业化应该是首选方式,这种方式始终有效。
if constexpr
(需要 c++17 或更高版本)为所有 typeId()
案例创建一个 Type
函数:
template<typename T>
inline constexpr Type typeId() /* noexcept */
{
if constexpr (std::is_same_v<T, int>) return Type_Int;
else if constexpr (std::is_same_v<T, double>) return Type_Double;
else if constexpr (std::is_same_v<T, const char*>
|| (std::is_array_v<T>
&& std::is_same_v<std::remove_const_t<std::remove_extent_t<T>>, char>)
)
return Type_String;
}
如果存在多维
char
数组,可以将上面的std::remove_extent_t
替换为std::remove_all_extents
。
使用 c++20,您可以使用概念并编写一个特征/检查,如
std::is_array
来检查类型是否为 char 数组,如下所示。
template<class T>
struct is_char_array : std::false_type {};
template<>
struct is_char_array<char[]> : std::true_type {};
template<std::size_t N>
struct is_char_array<char[N]> : std::true_type {};
template< class T >
inline constexpr bool is_char_array_v = is_char_array<T>::value;
template<typename T> concept checkArray = is_char_array_v<T>;
template<checkArray T> Type typeId()
{
std::cout << "char N version\n";
return Type_String;
}
关于:
template<int N> Type typeId(char(& a)[N]) { return Type_String; }
这不是完全相同的语法,但它的工作方式与另一种相同,因为您可以通过模板参数指定类型。
参见示例
顺便说一句,您根本不需要此映射的模板。具有基本功能重载的解决方案将起作用:
#include <iostream>
enum Type{
Type_Int,
Type_Double,
Type_String,
};
constexpr Type typeId(int) { return Type_Int; }
constexpr Type typeId(double){ return Type_Double; }
constexpr Type typeId(const char*) { return Type_String; }
int main(int argc, char** argv)
{
int e;
std::cout << typeId(e) << std::endl;
double d;
std::cout << typeId(d) << std::endl;
const char * c;
std::cout << typeId(c) << std::endl;
char b[10];
std::cout << typeId(b) << std::endl;
char a[11];
std::cout << typeId(a) << std::endl;
}
该函数可以是 constexpr,因此您也可以在编译时提取
Type
。
最后,这是一个根本没有功能的版本,因为它不是严格要求的。这具有最低的开销(理解:无)
#include <iostream>
enum Type{
Type_Int,
Type_Double,
Type_String,
};
template <typename T> constexpr Type Map;
template <> constexpr Type Map<int> = Type_Int;
template <> constexpr Type Map<double> = Type_Double;
template <> constexpr Type Map<const char*> = Type_String;
template <size_t N> constexpr Type Map<char[N]> = Type_String;
int main(int argc, char** argv)
{
std::cout << Map<int> << std::endl;
std::cout << Map<double> << std::endl;
std::cout << Map<const char*> << std::endl;
std::cout << Map<char[10]> << std::endl;
std::cout << Map<char[11]> << std::endl;
}
还有反转映射的完整示例(
Type
到实际的 C++ 类型)这里
此外,您不需要 C++20(无论如何可能在您的嵌入式开发工具箱中不可用)。