web-dev-qa-db-ja.com

メタプログラミング:新しい構造体をその場で宣言する

その場で新しい型(空のstruct、または実装のない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;
_

しかし、テンプレート化/メタについては、いくつかの魔法のmake_new_type()関数がいいでしょう。

ステートフルメタプログラミングが不正な形式 になったときに、そのようなことが可能になりますか?

14

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で可能かどうかは別の話です。

18

使用したい構文をほぼ得ることができます

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__>());

すべてのラムダ式が一意の型になるため、これは機能します。そのため、<>の一意の値ごとに、異なるクロージャーを返す異なる関数を取得します。

マクロを導入すると、__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);
22
NathanOliver

私は知っています...彼らは蒸留された悪です...しかし、これは古い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, "");
 }
2
max66