9

struct標準が単純な boolean ではなくtemplate として指定する理由はありますconstexprか?

enable_if主な質問に対する適切な回答でおそらく回答される追加の質問では、非構造体バージョンをどのように処理しますか?

4

5 に答える 5

19

1つの理由は、constexpr関数がネストされたメンバーを提供できないことtypeです。これは、一部のメタプログラミングの状況で役立ちます。

明確にするために、私は型を生成し、明らかに関数にすることができない変換特性(のような)だけについて話しているのではありません。すべての型特性は、単項型特性バイナリ型特性でさえ、そのようなネストされたメンバーを提供します。たとえば、です。make_unsignedconstexprtypeis_void<int>::typefalse_type

もちろん、これはで回避できますがstd::integral_constant<bool, the_constexpr_function_version_of_some_trait<T>()>、それほど実用的ではありません。

いずれにせよ、本当に関数のような構文が必要な場合は、それはすでに可能です。トレイトコンストラクターを使用して、constexprの暗黙的な変換をintegral_constant利用できます。

static_assert(std::is_void<void>(), "void is void; who would have thunk?");

変換特性の場合、テンプレートエイリアスを使用して、その構文に近いものを取得できます。

template <bool Condition, typename T = void>
using enable_if = typename std::enable_if<Condition, T>::type;
// usage:
// template <typename T> enable_if<is_void<T>(), int> f();
//
// make_unsigned<T> x;
于 2012-01-17T14:53:36.783 に答える
4

注:これは、適切な回答というよりも暴言のように見えます...ただし、以前の回答を読んで少しかゆくなったので、すみません;)

まず、クラスの特徴は歴史的にテンプレート構造で行われてきましconstexprdecltype。これら 2 つがなければ、関数を使用するのはもう少し面倒でしたが、さまざまなライブラリの実装でis_base_of 、継承を正しく行うために内部で関数を使用する必要がありました。

関数を使用する利点は何ですか?

  • 継承は機能します。
  • 構文はより自然になる可能性があります(typename ::type 愚かな TMに見えます)
  • かなりの数の特性が現在廃止されています

実際、継承はおそらくクラスの特性に対する主なポイントです。mommaのようにすべての派生クラスを特殊化する必要があるのは、非常に面倒です。とてもうるさい。関数を使用すると、特性を継承するだけで、必要に応じて特殊化できます

短所は何ですか?

  • 包装!構造体の特性は、一度に複数の型/定数を埋め込むことができます。

もちろん、これは実際には煩わしいと主張する人もいるかもしれませiterator_traitsん。さまざまな機能がこれを自然に提供します。std::iterator_traits

それはうまくいくでしょうか?

constexprまあ、 from を除いてすべてが基づいているという言葉でenable_if(しかし、それは特性ではありません)、あなたは行くでしょう:

template <typename T>
typename enable_if<std::is_integral(T()) and
                   std::is_signed(T())>::type

std::declval注:未評価のコンテキスト (つまり、ほとんど) が必要なため、ここでsizeofdecltype使用しませんでした。したがって、1 つの追加要件 (すぐには表示されません) は、それTがデフォルトで構築可能であることです。

本当に必要な場合は、ハックがあります。

#define VALUE_OF(Type_) decltype(std::declval<T>())

template <typename T>
typename enable_if<std::is_integral(VALUE_OF(T)) and
                   std::is_signed(VALUE_OF(T))>::type

また、定数ではなく型が必要な場合はどうすればよいですか?

decltype(common_type(std::declval<T>(), std::declval<U>()))

問題もありません(はい、ここでは を使用していますdeclval)。しかし...型を渡すことは何の関係もありませんconstexpr; 関数は、興味のある値constexprを返す場合に役立ちます。複雑な型を返す関数はもちろん使用できますが、そうではなく、型の値を使用しません。constexpr

また、trais と type をチェーンする必要がある場合はどうすればよいでしょうか?

皮肉なことに、これは関数が輝く場所です:)

// class version
template <typename Container>
struct iterator { typedef typename Container::iterator type; };

template <typename Container>
struct iterator<Container const> {
  typedef typename Container::const_iterator type;
};

template <typename Container>
struct pointer_type {
  typedef typename iterator<Container>::type::pointer_type type;
};


template <typename Container>
typename pointer_type<Container>::type front(Container& c);

// Here, have a cookie and a glass of milk for reading so far, good boy!
// Don't worry, the worse is behind you.


// function version
template <typename Container>
auto front(Container& c) -> decltype(*begin(c));

何!詐欺師!特性が定義されていません!

うーん...実際、それがポイントです。ではdecltype、かなりの数の特性が冗長になりました。

ドライ

継承はうまくいきます!

基本的なクラス階層を見てみましょう:

struct Base {};
struct Derived: Base {};
struct Rederived: Derived {};

そして特性を定義します:

// class version
template <typename T>
struct some_trait: std::false_type {};

template <>
struct some_trait<Base>: std::true_type {};

template <>
struct some_trait<Derived>: some_trait<Base> {}; // to inherit behavior

template <>
struct some_trait<Rederived>: some_trait<Derived> {};

注:トレイト forDerivedは、直接宣言するのではなくtruefalse祖先から動作を取得することを意図しています。このように、祖先がスタンスを変更すると、階層全体が自動的に追従します。ほとんどの場合、基本機能は祖先によって提供されるため、その特性に従うことは理にかなっています。型特性についてはなおさらです。

// function version
constexpr bool some_trait(...) { return false; }

constexpr bool some_trait(Base const&) { return true; }

: 省略記号の使用は意図的なものであり、これはキャッチオール オーバーロードです。テンプレート関数は、他のオーバーロードよりも適切に一致します (変換は必要ありません) が、省略記号は常に最悪の一致であり、他のオーバーロードが適していないものだけを選択することを保証します。

後者のアプローチがどれほど簡潔かを正確に言う必要はないと思いますか?混乱を取り除くだけでなくtemplate <>、無料で継承も取得できます。

enable_ifそう実装できますか?

残念ながらそうは思いませんが、すでに述べたように、これは特性ではありません。そして、バージョンは型ではなく引数を使用するため、stdうまく機能します:)constexprbool

だからなぜ

唯一の技術的な理由は、コードの大部分が、歴史的に型 ( std::numeric_limit) として提供されていた多くの特性にすでに依存しているため、一貫性がそれを決定することです。

さらに、移行がboost::is_*非常に簡単になります。

個人的には残念だと思います。しかし、私は平均的な企業よりも、自分が書いた既存のコードをレビューすることにおそらく熱心です。

于 2012-01-17T18:51:35.297 に答える
3

1つの理由は、type_traitsプロポーザルがconstexprプロポーザルよりも古いことです。

もう1つは、必要に応じて、独自のタイプの特殊化を追加できることです。

于 2012-01-17T14:54:05.003 に答える
2

主な理由は、それtype_traitsがすでに の一部でtr1あったため、基本的にほぼ同じ形式で標準に組み込まれることが保証されていたため、constexpr. その他の考えられる理由は次のとおりです。

  • 特性を型として持つことで、特性の型で関数をオーバーロードできます
  • 多くの特性 ( など) は aではなくa をremove_pointer定義するため、この方法で表現する必要があります。値を定義する特性と型を定義する特性に異なるインターフェースを持つことは不必要に思えるtypevalue
  • templated structs関数は部分的に特殊化できますが、関数はできません。そのため、一部の特性の実装が容易になる可能性があります

2 番目の質問について: enable_ifa を定義するようにtype(false が渡された場合はそうではありません)、 a 内のネストされた typedefstructが実際に進むべき道です

于 2012-01-17T14:57:00.010 に答える
2

おそらく、ブーストにはテンプレートで実装された type_traits のバージョンが既にあったためです。

そして、標準化委員会のメンバーがどれだけコピーを後押ししているかは誰もが知っています。

于 2012-01-17T14:50:43.733 に答える