元编程:动态声明新的结构

时间:2019-03-25 16:01:37

标签: c++ templates metaprogramming stateful compile-time-constant

是否可以即时声明一个新类型(一个空的struct,或者没有实现的结构)?

例如

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, "");

“手动”解决方案是

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;

但是对于模板/ meta来说,一些神奇的make_new_type()函数会很好。

stateful metaprogramming is ill-formed现在可以实现类似的功能吗?

3 个答案:

答案 0 :(得分:22)

您几乎可以获得所需的语法

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表达式都会产生一个唯一的类型。因此,对于<>中的每个唯一值,您将获得一个不同的函数,该函数返回不同的闭包。

如果您引入了宏,则可以不必像这样键入__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);

答案 1 :(得分:18)

在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类放在匿名名称空间中是一个严重的错误:这样,类型在翻译中将不再唯一单位。在您计划发货前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中这种魔术是否可能是另一回事。

答案 2 :(得分:2)

我知道...他们是邪恶的蒸馏物...但是在我看来,这是一个适用于旧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, "");
 }