我将以我想像的方式使用我想创建的代码开始。它不必完全像这样,但这是标题中“简洁”的一个很好的例子。就我而言,它是一种类型到相关枚举值的映射。
struct bar : foo<bar, foo_type::bar> { /* ... */ };
// \_/ \___________/
// ^ Type ^ Value
理想情况下,该操作是使用继承语法和适当的模板参数自动注册foo
的第一个模板参数(类型)和第二个值(第二个值)之间的双向映射,以便以后可以执行示例中的操作以下。foo_type value = to_value<bar>; // Should be foo_type::bar
using type = to_type<foo_type::bar>; // Should be bar
我知道我可以为每个“类型-值”对手动编写两个模板专长来执行此操作,但是我想知道如果不使用宏,它是否可以比这少那么乏味。我已经尝试过的是...
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;
};
};
然后将其与我在开始时介绍的内容类似地使用。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上显示以下错误。我觉得没有明确的手动专业知识可能无法实现,但是C++ 17允许很多令人惊讶的基于模板的黑客攻击,因此在我放弃这个主意之前,请与更多有经验的人确认。
最佳答案
正如@yeputons所说, friend 注入(inject)可以在这里提供帮助。这是一个令人毛骨悚然的功能,我不能说我完全理解它是如何工作的,但是就可以了。
#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!
}
Run on gcc.godbolt.org
它似乎可以同时在GCC,Clang和MSVC上使用。
我使用的是
auto
模板参数,因此您可以将不同的类型映射到来自不同枚举的常量,甚至映射到普通整数。将其约束为仅接受单个特定的枚举应该很容易,并且留给读者练习。当然,对于类型到枚举的映射,您可以简单地将
static constexpr
成员变量添加到foo
。但是对于枚举到类型的映射,我不知道有什么替代 friend 注入(inject)的好方法。关于c++ - 简洁双向静态1 :1 mapping of values and types,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62448834/