使用变量参数进行模板专业化

问题描述 投票:0回答:4

我正在尝试将 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

c++ templates type-traits specialization function-templates
4个回答
8
投票

你不能部分特化一个函数。但你可以部分专门化类或结构。

因此,要解决此问题,请在类中执行模板逻辑,然后在函数中使用它:

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


4
投票

我该如何识别所有

char[N]
类型?

班级专业化应该是首选方式,这种方式始终有效。

但是,在您的情况下,您可以按如下方式使用

if constexpr
(需要 或更高版本)为所有
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


1
投票

使用 ,您可以使用概念并编写一个特征/检查,如

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;  
  }

工作演示


1
投票

关于:

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(无论如何可能在您的嵌入式开发工具箱中不可用)。

© www.soinside.com 2019 - 2024. All rights reserved.