是否可以使用 constexpr if 来检查类型是容器还是 std::string

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

例如我有这门课:

template <typename T, U>
class TLVParser
{
public:
    TLVParser(T value) : value_(std::move(value)) {}
    void parse(const std::span<uint8_t> &buffer, size_t &offset)
    {
        if constexpr (std::is_arithmetic_v<T>)
        {
            return parsePrimitive(buffer, offset);
        }
        else if constexpr
            constexpr(std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
            {
                return parseStringView(buffer, offset);
            }
        else if constexpr (std::is_same_v<T, std::span<U>> || std::is_same_v<T, std::vector<U>>)
        {
            return parseContainer(buffer, offset);
        }
        else
        {
            static_assert(always_false<T>::value, "Unsupported type for TLV parsing");
        }
    }

private:
    T value_;
};

据我了解,

string_view
std::string
std::vector
std::span
有一些共同的属性,我可以使用相同的解析函数。那么有没有一种方法可以使用
if constexpr
来检查这种类型是否是像这样的容器,而无需为每个类型编写
||
,而只需使用一个概念来保证该容器应该具有特定的属性,例如能够范围循环?

我的第二个问题是我可以为

std::span<uint8_t>
参数创建一个类型名称,以便我可以使用
std::vector
std::array
甚至
uint8_t*
char*
吗?

c++ templates c++20 metaprogramming
1个回答
0
投票

正如一些人在评论中已经指出的那样,确定什么构成“容器”通常是问题所在。一种方法是检查它是否支持“std::begin()”和“std::end()”,它也处理 C 样式数组,而不是只检查具有成员“begin( )”和“end()”(这会忽略 C 样式数组)。

这是另一种技术,但我更喜欢它,因为我不会在这里讨论。它只是检查除了 C 样式数组之外,它是否是一个具有“iterator”或“const_iterator”别名(typedef)的类。宏的使用很丑陋,但它只是为了创建类型安全的代码来完成检查“iterator”或“const_iterator”的工作(该代码也可以追溯到很长一段时间并支持 C+ 之前的版本+20,尽管“_v”辅助变量仅适用于 C++17 或更高版本)。当然,现在也可以使用一些概念,因此对于那些想要实现更现代的东西的人来说,here的一些课程也可以被利用(但我不会在这里开始解决这个问题)。

无论如何,以下代码本身不符合标准,但在实践中效果很好(是的,如果需要,它会处理“std::string”,因为它具有必要的迭代器别名)。单击此处运行它。

#include <type_traits>
#include <vector>
#include <iostream>

#define DECLARE_HAS_MEMBER_TYPE(NAME) \
    template <typename, typename = void> \
    struct HasMemberType_##NAME : std::false_type \
    { \
    }; \
    template <typename T> \
    struct HasMemberType_##NAME<T, std::void_t<typename T::NAME>> : std::true_type \
    { \
    }; \
    template <typename T> \
    inline constexpr bool HasMemberType_##NAME##_v = HasMemberType_##NAME<T>::value;

DECLARE_HAS_MEMBER_TYPE(iterator)
DECLARE_HAS_MEMBER_TYPE(const_iterator)

template <typename T>
using IsContainer = std::bool_constant<HasMemberType_const_iterator_v<T> ||
                                       HasMemberType_iterator_v<T> ||
                                       std::is_array_v<T>>;

template <typename T>
inline constexpr bool IsContainer_v = IsContainer<T>::value;                                       

int main()
{
    std::cout << std::boolalpha << IsContainer_v<std::vector<int>> << "\n"; // true
    std::cout << std::boolalpha << IsContainer_v<int []> << "\n"; // true (C-style array)
    std::cout << std::boolalpha << IsContainer_v<int *> << "\n"; // false (pointer not considered a container)
    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.