值和类型的简洁双向静态 1:1 映射
Concise bidirectional static 1:1 mapping of values and types
我将从我想象如何使用我想创建的代码开始。它不必完全像这样,但它是一个很好的例子,说明了我在标题中所说的"简洁"的意思。在我的例子中,它是将类型映射到相关的枚举值。
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
我知道我可以为每个类型-值对手动编写两个模板专用化来执行此操作,但我想知道它是否可以比不使用宏时更乏味。
我已经尝试过的是...
专用- 模板别名,以编写更少的代码来生成专用化。在当前的C++版本(17/20(中显然是不可能的。
- 专用继承的模板成员类型。
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 上失败并出现以下错误。
明确的专业化;"foo_base::to_value"已被实例化
"foo_base::to_value":无法在当前范围内专用化模板
我觉得如果没有明确的手动专业化,它可能不可行,但 C++17 允许许多令人惊讶的基于模板的黑客,所以在我放弃这个想法之前,想与更有经验的人确认。
正如@yeputons所说,朋友注入可以在这里提供帮助。这是一个令人毛骨悚然的功能,我不能说我完全理解它是如何工作的,但它就在这里。
#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.godbolt.org 上运行
它似乎适用于GCC,Clang和MSVC。
我使用的是auto
模板参数,因此您可以将不同类型的映射到来自不同枚举的常量,甚至是纯整数。将其限制为仅接受单个特定枚举应该很容易,并且留给读者作为练习。
当然,对于类型到枚举的映射,您可以简单地将static constexpr
成员变量添加到foo
。但是我不知道枚举到类型映射的朋友注入有什么好的替代方案。
@HolyBlackCat的回答太棒了。类型到枚举可以通过比 ADL hackery 更简单的方式实现,所以我试图将枚举到类型位提炼到最低限度:
template <auto E>
struct adl_to_type
{
friend auto foo_type_to_type(adl_to_type);
};
template<typename T, foo_type E>
struct foo
{
friend auto foo_type_to_type(adl_to_type<E>) { return (T*)nullptr; };
};
template <foo_type E>
using to_type = std::remove_pointer_t<decltype(foo_type_to_type(adl_to_type<E>{}))>;
int main()
{
to_type<foo_type::bar>{}.say();
return 0;
}
在 gcc.godbolt.org 上运行
它仍然让我大吃一惊。auto
返回类型在这里绝对至关重要。即使将其更改为foo
中的T*
也会产生编译错误。我还尝试摆脱adl_to_type
并使用integral_constant
,但似乎将foo_type_to_type
声明为用于解析 ADL 的类型中的friend 函数是这里的关键。
- 初始化之前使用的静态映射
- 值和类型的简洁双向静态 1:1 映射
- 如果包含映射的静态库与可执行文件和动态库链接,静态映射(变量)是否会被多次释放?
- 线程时访问静态映射时出现隔离错误
- 具有不同参数列表的模板对象的模板静态映射
- 静态映射超出堆栈
- C++ 已停止工作静态映射函数
- 我们如何在模板类中使用静态映射,模板类的每个实例化都应该使用相同的映射
- 如何从 C++ 中的单独类访问静态映射?
- 在 C++ 中使用静态映射的奇怪问题
- C++初始化对象的静态映射
- 使用静态映射在C++中缓存数据的推荐方法是什么
- C++中的静态映射
- 静态映射中动态分配的对象.删除必要的
- 静态映射初始化
- (VS2015)正在尝试用初始值设定项列表中的数据填充静态映射
- C++模板静态映射初始化
- 检查 C++ 中的空静态映射变量
- 静态映射为C++中的类成员
- 在不同函数内的类中使用静态映射: