简洁的双向静态1

简洁的双向静态1

本文介绍了简洁的双向静态1:1值和类型映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我将以我想像的方式使用我想创建的代码开始。不一定非要这样,但这是我所说的简洁的一个很好的例子。在标题中。在我的情况下,它是将类型映射到相关的枚举值。

I'm going to start with how I imagine using the code I'd like to create. It doesn't have to be exactly like this but it's a good example of what I mean by "concise" in the title. In my case it's mapping of a type to a related enumeration value.

struct bar : foo<bar, foo_type::bar> { /* ... */ };
//               \_/  \___________/
//                ^ Type         ^ Value

什么理想情况下,这应该使用继承语法和适当的模板自动注册 foo 的第一个模板参数和类型以及第二个值之间的双向映射。参数,以便以后可以执行下面的示例。

What this should ideally do is an automatic registration of a bidirectional mapping between the first template parameter of foo, a type, and second, a value, just with the inheritance syntax and proper template parameters, so that I can later do what's in the example below.

foo_type value = to_value<bar>; // Should be foo_type::bar
using type = to_type<foo_type::bar>; // Should be bar

我知道我可以为每个类型-值对手动编写两个模板专业化代码,但是我我想知道如果不使用宏,它是否会比那么少乏味。

I know I could manually write two template specializations per type-value pair to do this but I'm wondering if it could be less tedious than that without using macros.

我已经尝试过的是...

What I tried already is...


  1. 专业化模板别名,以减少编写专业代码的代码。在当前的C ++版本(17/20)中显然不可能。

  2. 对继承的模板成员类型进行专业化。


struct foo_base
{
    template<typename T>
    struct to_value
    {};

    template<foo_type E>
    struct to_type
    {};
};

template<typename T, foo_type E>
struct foo : public foo_base
{
    template<>
    struct to_value<T>
    {
        static constexpr auto value = E;
    };

    template<>
    struct to_type<E>
    {
        using type = T;
    };
};

然后将其与我在开始时介绍的内容类似地使用。

It would then be used similarily to what I presented at the beginning.

foo_type value = foo_base::to_value<bar>::value; // Should be foo_type::bar
using type = foo_base::to_type<foo_type::bar>::type; // Should be bar

但是它失败,并在MSVC上出现以下错误。

But it fails with errors below on MSVC.

'foo_base :: to_value':无法在当前范围内专门化模板

'foo_base::to_value': cannot specialize template in current scope

我觉得如果没有明确的手动专业知识可能无法实现,但是C ++ 17允许很多令人惊讶的基于模板的黑客攻击,因此想在我放弃这个想法之前先与经验丰富的人确认一下。

I feel like it might not be doable without explicit manual specializations, but C++17 allows a lot of surprising template based hacks, so want to confirm with more experienced people before I drop the idea.

推荐答案

正如@yeputons所说,朋友注入在这里可以提供帮助。这是一个令人毛骨悚然的功能,我不能说我完全理解它的工作原理,但是在这里就行了。

As @yeputons said, friend-injection can help here. It's a spooky feature, and I can't say I fully understand how it works, but here it goes.

#include <iostream>
#include <type_traits>

template <typename T>
struct tag {using type = T;};

template <typename T>
struct type_to_enum_friend_tag
{
    friend constexpr auto adl_type_to_enum(type_to_enum_friend_tag);
};
template <auto E>
struct enum_to_type_friend_tag
{
    friend constexpr auto adl_enum_to_type(enum_to_type_friend_tag);
};

namespace impl
{
    // Would've used `= delete;` here, but GCC doesn't like it.
    void adl_type_to_enum() {}
    void adl_enum_to_type() {}
}

template <typename T>
constexpr auto type_to_enum_helper()
{
    // Make sure our ADL works even if some stray
    // identifier named `adl_type_to_enum` is visible.
    using impl::adl_type_to_enum;
    return adl_type_to_enum(type_to_enum_friend_tag<T>{});
}
template <typename T>
inline constexpr auto type_to_enum = type_to_enum_helper<T>();

template <auto E>
constexpr auto enum_to_type_helper()
{
    // Make sure our ADL works even if some stray
    // identifier named `adl_type_to_enum` is visible.
    using impl::adl_enum_to_type;
    return adl_enum_to_type(enum_to_type_friend_tag<E>{});
}
template <auto E>
using enum_to_type = typename decltype(enum_to_type_helper<E>())::type;


template <typename T, auto E>
struct foo
{
    friend constexpr auto adl_type_to_enum(type_to_enum_friend_tag<T>)
    {
        return E;
    }
    friend constexpr auto adl_enum_to_type(enum_to_type_friend_tag<E>)
    {
        return tag<T>{};
    }
};

enum class foo_type {bar = 42};
struct bar : foo<bar, foo_type::bar>
{
    void say() {std::cout << "I'm bar!\n";}
};

int main()
{
    std::cout << int(type_to_enum<bar>) << '\n'; // 42
    enum_to_type<foo_type::bar>{}.say(); // I'm bar!
}

它似乎可以在GCC,Clang和MSVC上使用。

It appears to work on both GCC, Clang, and MSVC.

我正在使用 auto template参数,因此您可以将不同的类型映射到来自不同枚举的常量,甚至映射到普通整数。将其约束为仅接受一个特定的枚举应该很容易,并且留给读者练习。

I'm using an auto template parameter, so you can map different types to constants from different enums, or even to plain integers. Constraining this to accept only a single specific enum should be easy, and is left as an exercise to the reader.

当然,对于类型到枚举的映射,您只需将静态constexpr 成员变量添加到 foo 。但是,对于枚举到类型的映射,我不知道可以使用任何其他方法来代替朋友注入。

Of course, for the type-to-enum mapping you could simply add a static constexpr member variable to foo. But I don't know any good alternatives to friend-injection for the enum-to-type mapping.

这篇关于简洁的双向静态1:1值和类型映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 07:49