std :: decayとは何ですか、いつ使用する必要がありますか?


185

の存在の理由は何ですか std::decayですか?どのような状況でstd::decay役立ちますか?


3
スレッドに引数を渡すときなど、標準ライブラリで使用されます。これらは値で格納する必要があるため、配列などは格納できません。代わりに、ポインタが格納されます。また、関数のパラメータータイプの調整を模倣するメタ関数でもあります。
dyp

3
decay_t<decltype(...)>autoが推測されるかを見るには、素晴らしい組み合わせです。
Marc Glisse 2014

58
放射性変数?:)
saiarcot895 2014

7
std :: decay()は3つのことを実行できます。1 Tの配列をT *に変換できます。2. cv修飾子と参照を削除できます。3.関数TをT *に変換します。例:decay(void(char))-> void(*)(char)。回答の3番目の使用法については誰も言及していないようです。
r0ng 2017年

1
C ++にはまだクォークがありません
Wormer 2018

回答:


192

<joke>これは明らかに、放射性std::atomicタイプを非放射性タイプに崩壊させるために使用されます。</ joke>

N2609は提案した論文ですstd::decay。紙は説明します:

簡単に言えdecay<T>::typeば、Tが配列型または関数型への参照である場合を除いて、ID型変換です。これらの場合decay<T>::type、それぞれ、ポインタまたは関数へのポインタが生成されます。

動機付けの例はC ++ 03 std::make_pairです。

template <class T1, class T2> 
inline pair<T1,T2> make_pair(T1 x, T2 y)
{ 
    return pair<T1,T2>(x, y); 
}

文字列リテラルを機能させるために、パラメーターを値で受け入れました。

std::pair<std::string, int> p = make_pair("foo", 0);

参照によりパラメーターを受け入れた場合T1は、配列型として推定され、次にpair<T1, T2>ます。

しかし、これは明らかに重大な非効率につながります。したがって、の必要性はdecay、値渡しが発生したときに発生する一連の変換を適用して、参照によるパラメーターの取得効率を高めながら、コードが文字列リテラルを処理するために必要な型変換を取得できるようにするためです。配列型、関数型など:

template <class T1, class T2> 
inline pair< typename decay<T1>::type, typename decay<T2>::type > 
make_pair(T1&& x, T2&& y)
{ 
    return pair< typename decay<T1>::type, 
                 typename decay<T2>::type >(std::forward<T1>(x), 
                                            std::forward<T2>(y)); 
}

注:これは実際のC ++ 11 make_pair実装ではありません-C ++ 11 make_pairstd::reference_wrappersをアンラップします。


「T1は配列型として推定され、次にペア<T1、T2>を作成することは不正な形式になります。」ここで問題は何ですか?
カミノ2016年

6
このようにして、4文字の文字列のみを受け入れることができるpair <char [4]、int>を取得します
camino

@caminoわかりません。std:: decayがないと、ペアの最初の部分は、charへの1つのポインタではなく、4つのcharsに4バイトを使用することになりますか?それはstd :: forwardが行うことですか?配列からポインタへの減衰を止めますか?
ゼブラフィッシュ2017年

3
@ゼブラフィッシュ配列崩壊です。例:template <typename T> void f(T&); f( "abc"); Tはchar(&)[4]ですが、template <typename T> void f(T); f( "abc"); Tはchar *です。また、ここでの説明を見つけることができます:stackoverflow.com/questions/7797839/...
カミノ

69

テンプレート型のパラメーターを取るテンプレート関数を扱う場合、多くの場合、ユニバーサルパラメーターがあります。ユニバーサルパラメータは、ほとんどの場合、何らかの並べ替えの参照です。それらはconst-volatileにも適合しています。そのため、ほとんどのタイプトレイトは期待どおりに機能しません。

template<class T>
void func(T&& param) {
    if (std::is_same<T,int>::value) 
        std::cout << "param is an int\n";
    else 
        std::cout << "param is not an int\n";
}

int main() {
    int three = 3;
    func(three);  //prints "param is not an int"!!!!
}

http://coliru.stacked-crooked.com/a/24476e60bd906bed

ここでの解決策は使用することstd::decayです:

template<class T>
void func(T&& param) {
    if (std::is_same<typename std::decay<T>::type,int>::value) 
        std::cout << "param is an int\n";
    else 
        std::cout << "param is not an int\n";
}

http://coliru.stacked-crooked.com/a/8cbd0119a28a18bd


14
私はこれに満足していません。decay非常に攻撃的です。たとえば、配列への参照に適用すると、ポインターが生成されます。通常、この種のメタプログラミングIMHOにはあまりにも積極的です。
dyp

@dyp、それでは「アグレッシブ」とは何でしょうか。代替案は何ですか?
セルジュロガッチ2017

5
@SergeRogatch「ユニバーサルパラメータ」/ユニバーサル参照/転送参照のremove_const_t< remove_reference_t<T> >場合、カスタムメタ関数にラップされる可能性があります。
dyp 2017

1
paramはどこで使用されていますか?これはfuncの引数ですが、どこでも使用されているとは思いません
savram

2
@savram:これらのコードでは、そうではありません。値ではなくタイプのみをチェックしています。パラメータの名前を削除すれば、すべてがうまくいくはずです。
Mooing Duck 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.