首页 > 解决方案 > c ++ 20如何制作一个像容器一样的约束元组,它只包含允许的类型和自身的一个实例

问题描述

想象一下,我想制作一个像模板容器一样的元组作为 api 接口的一部分。我想将其限制为允许的类型列表以及此模板容器的实例。现在我有这样的事情:

#include <string>
#include <tuple>
#include <utility>
#include <type_traits>

template<typename T>
constexpr const bool bIsAllowedType =
    std::is_same<T, bool>::value ||
    std::is_same<T, void*>::value ||
    std::is_same<T, double>::value ||
    std::is_same<T, int64_t>::value ||
    std::is_same<T, std::string>::value;

template<typename T, typename... Args>
constexpr const bool bIsAllowedArgList =
    bIsAllowedType<std::remove_reference_t<T>> &&
    bIsAllowedArgList<Args...>;

template<typename T>
constexpr const bool bIsAllowedArgList<T> =
    bIsAllowedType<std::remove_reference_t<T>>;

template<typename... Args>
concept CAllowedArgList =
    bIsAllowedArgList<Args...>;

template<CAllowedArgList... Args>
class Container
{
private:
    using TupleT = std::tuple<std::decay_t<Args>...>;
    TupleT data;
public:
    Container() = default;
    Container(Args&&... args) : data(std::forward<Args>(args)...) {}
};

int main()
{
    auto test_cont = Container(1LL, 2.0, std::string("three")); // Ok
    auto err_cont = Container(std::wstring(L"wide string")); // Not ok, type's not allowed
    return 0;
}

那么,我如何让它也接受它自己的一个实例呢?像这样:

int main()
{
    auto test_cont = Container(1LL, Container(2.0, std::string("three"))); 

    return 0;
}

我希望它尽可能简单,所以请尽可能限制使用第三方库(如 Boost)

标签: c++c++20c++-concepts

解决方案


您可以使用部分模板专业化来检查这一点。

template <typename T>
constexpr const bool bIsContainerType = false;

template <typename... Types>
constexpr const bool bIsContainerType<Container<Types...>> = true;

然后我们只需将其添加到您的签到列表中bIsAllowedType

template<typename T>
constexpr const bool bIsAllowedType =
    std::is_same<T, bool>::value ||
    std::is_same<T, void*>::value ||
    std::is_same<T, double>::value ||
    std::is_same<T, int64_t>::value ||
    std::is_same<T, std::string>::value ||
    bIsContainerType<T>;

由于bIsAllowedType现在需要知道Container哪些需要了解我们的概念,因此我将其更改bIsAllowedType为结构,以便我们可以转发声明它。

#include <string>
#include <tuple>
#include <utility>
#include <type_traits>

template<typename T>
struct bIsAllowedType;

template<typename... T>
constexpr const bool bIsAllowedArgList =
    (bIsAllowedType<std::remove_reference_t<T>>::value && ...);

template<typename... Args>
concept CAllowedArgList =
    bIsAllowedArgList<Args...>;

template<CAllowedArgList... Args>
class Container
{
private:
    using TupleT = std::tuple<std::decay_t<Args>...>;
    TupleT data;
public:
    Container() = default;
    Container(Args&&... args) : data(std::forward<Args>(args)...) {}
};

template <typename T>
constexpr const bool bIsContainerType = false;

template <typename... Types>
constexpr const bool bIsContainerType<Container<Types...>> = true;

template<typename T>
struct bIsAllowedType {
    static constexpr bool value =
        std::is_same<T, bool>::value ||
        std::is_same<T, void*>::value ||
        std::is_same<T, double>::value ||
        std::is_same<T, int64_t>::value ||
        std::is_same<T, std::string>::value ||
        bIsContainerType<T>;
};


int main()
{
    auto test_cont = Container(static_cast<int64_t>(1), 2.0, std::string("three")); // Ok
    //auto err_cont = Container(std::wstring(L"wide string")); // Not ok, type's not allowed
    auto test_cont2 = Container(static_cast<int64_t>(1), Container(2.0, std::string("three"))); 
    return 0;
}

现场演示


推荐阅读