元编程:即时声明一个新结构
Meta programming: Declare a new struct on the fly
是否可以即时声明新类型(空结构或没有实现的结构)?
例如
constexpr auto make_new_type() -> ???;
using A = decltype(make_new_type());
using B = decltype(make_new_type());
using C = decltype(make_new_type());
static_assert(!std::is_same<A, B>::value, "");
static_assert(!std::is_same<B, C>::value, "");
static_assert(!std::is_same<A, C>::value, "");
一个"manual"的解决方案是
template <class> struct Tag;
using A = Tag<struct TagA>;
using B = Tag<struct TagB>;
using C = Tag<struct TagC>;
甚至
struct A;
struct B;
struct C;
但是对于模板化/元数据来说,一些神奇的 make_new_type()
函数会很好。
现在 这样的事情有可能吗?
我知道...它们是邪恶的提炼...但在我看来,这是旧 C 风格宏的作品
#include <type_traits>
#define newType(x) \
struct type_##x {}; \
using x = type_##x;
newType(A)
newType(B)
newType(C)
int main ()
{
static_assert(!std::is_same<A, B>::value, "");
static_assert(!std::is_same<B, C>::value, "");
static_assert(!std::is_same<A, C>::value, "");
}
您几乎可以使用
获得您想要的语法
template <size_t>
constexpr auto make_new_type() { return [](){}; }
using A = decltype(make_new_type<__LINE__>());
using B = decltype(make_new_type<__LINE__>());
using C = decltype(make_new_type<__LINE__>());
这是可行的,因为每个 lambda 表达式都会产生唯一的类型。因此,对于 <>
中的每个唯一值,您会得到一个不同的函数,该函数 returns 是一个不同的闭包。
如果你引入一个宏,你就不必像
那样输入__LINE__
template <size_t>
constexpr auto new_type() { return [](){}; }
#define make_new_type new_type<__LINE__>()
using A = decltype(make_new_type);
using B = decltype(make_new_type);
using C = decltype(make_new_type);
在 C++20 中:
using A = decltype([]{}); // an idiom
using B = decltype([]{});
...
这是惯用的代码:这就是在 C++20 中写“给我一个独特的类型”的方式。
在 C++11 中,最清晰和最简单的方法是使用 __LINE__
:
namespace {
template <int> class new_type {};
}
using A = new_type<__LINE__>; // an idiom - pretty much
using B = new_type<__LINE__>;
匿名命名空间是最重要的一点。不把 new_type
class 放在匿名命名空间中是一个严重的错误:类型那么跨翻译单元将不再是唯一的。在您计划发货前 15 分钟,各种欢闹会接踵而至:)
这扩展到 C++98:
namespace {
template <int> class new_type {};
}
typedef new_type<__LINE__> A; // an idiom - pretty much
typedef new_type<__LINE__> B;
另一种方法是手动链接类型,让编译器静态验证链接是否正确完成,如果不正确则报错。所以它不会变脆(假设魔法成功)。
类似于:
namespace {
struct base_{
using discr = std::integral_type<int, 0>;
};
template <class Prev> class new_type {
[magic here]
using discr = std::integral_type<int, Prev::discr+1>;
};
}
using A = new_type<base_>;
using A2 = new_type<base_>;
using B = new_type<A>;
using C = new_type<B>;
using C2 = new_type<B>;
只需要一点魔法就可以确保类型为 A2 和 C2 的行不会编译。这种魔法在 C++11 中是否可行是另一回事。
是否可以即时声明新类型(空结构或没有实现的结构)?
例如
constexpr auto make_new_type() -> ???;
using A = decltype(make_new_type());
using B = decltype(make_new_type());
using C = decltype(make_new_type());
static_assert(!std::is_same<A, B>::value, "");
static_assert(!std::is_same<B, C>::value, "");
static_assert(!std::is_same<A, C>::value, "");
一个"manual"的解决方案是
template <class> struct Tag;
using A = Tag<struct TagA>;
using B = Tag<struct TagB>;
using C = Tag<struct TagC>;
甚至
struct A;
struct B;
struct C;
但是对于模板化/元数据来说,一些神奇的 make_new_type()
函数会很好。
现在
我知道...它们是邪恶的提炼...但在我看来,这是旧 C 风格宏的作品
#include <type_traits>
#define newType(x) \
struct type_##x {}; \
using x = type_##x;
newType(A)
newType(B)
newType(C)
int main ()
{
static_assert(!std::is_same<A, B>::value, "");
static_assert(!std::is_same<B, C>::value, "");
static_assert(!std::is_same<A, C>::value, "");
}
您几乎可以使用
获得您想要的语法template <size_t>
constexpr auto make_new_type() { return [](){}; }
using A = decltype(make_new_type<__LINE__>());
using B = decltype(make_new_type<__LINE__>());
using C = decltype(make_new_type<__LINE__>());
这是可行的,因为每个 lambda 表达式都会产生唯一的类型。因此,对于 <>
中的每个唯一值,您会得到一个不同的函数,该函数 returns 是一个不同的闭包。
如果你引入一个宏,你就不必像
那样输入__LINE__
template <size_t>
constexpr auto new_type() { return [](){}; }
#define make_new_type new_type<__LINE__>()
using A = decltype(make_new_type);
using B = decltype(make_new_type);
using C = decltype(make_new_type);
在 C++20 中:
using A = decltype([]{}); // an idiom
using B = decltype([]{});
...
这是惯用的代码:这就是在 C++20 中写“给我一个独特的类型”的方式。
在 C++11 中,最清晰和最简单的方法是使用 __LINE__
:
namespace {
template <int> class new_type {};
}
using A = new_type<__LINE__>; // an idiom - pretty much
using B = new_type<__LINE__>;
匿名命名空间是最重要的一点。不把 new_type
class 放在匿名命名空间中是一个严重的错误:类型那么跨翻译单元将不再是唯一的。在您计划发货前 15 分钟,各种欢闹会接踵而至:)
这扩展到 C++98:
namespace {
template <int> class new_type {};
}
typedef new_type<__LINE__> A; // an idiom - pretty much
typedef new_type<__LINE__> B;
另一种方法是手动链接类型,让编译器静态验证链接是否正确完成,如果不正确则报错。所以它不会变脆(假设魔法成功)。
类似于:
namespace {
struct base_{
using discr = std::integral_type<int, 0>;
};
template <class Prev> class new_type {
[magic here]
using discr = std::integral_type<int, Prev::discr+1>;
};
}
using A = new_type<base_>;
using A2 = new_type<base_>;
using B = new_type<A>;
using C = new_type<B>;
using C2 = new_type<B>;
只需要一点魔法就可以确保类型为 A2 和 C2 的行不会编译。这种魔法在 C++11 中是否可行是另一回事。