2017-05-23 4 views
1

私はジェイソン・ターナーのC++ Weekly - Ep 64 - C++11's std::min (and my version)のstd ::パラメータパックと分

を見ていたそして今、私はstd::common_typeを使用して、複数のタイプのために働くために、パラメータパックの拡張を行うためにここにガチョウチェイスを作り始め:

template <typename T, typename U> 
const typename std::common_type<T, U>::type& 
multi_type_min(const T& t, const U& u) 
{ 
    return t < u ? t : u; 
} 

template<typename First, typename ...T> 
typename std::common_type< First, T...>::type 
variadic_min(const First& f, const T& ...t) 
{ 
    const typename std::common_type< First, T...>::type* retVal =&f; 
    ((retVal= &multi_type_min(*retVal, t)), ...); 

    return *retVal; 
} 

これはどのように達成するのですか?私は何か愚かなhereをやっていますか?

答えて

2

可変引数のstd ::分std::common_typeで実装良い考えではありません。

署名されていないタイプと署名されたタイプを比較するとどうなりますか?あなたは、署名されたものが署名されていないと思っていますか?符号付き整数を符号なし整数に変換すると、非常に大きな正の値にラップする可能性があります。符号なし整数を符号付き整数に変換すると、オーバーフローするリスクがあります。

std::common_typeとの比較を再帰的に実行すると、シーケンスのstd::common_typeは、隣接する2つのペアの間で同じstd::common_typeではない場合があります。例えば

auto res = variadic_min(1, -211, 3, -63, 89u); 

1, -211, 3, -63, 89ustd::common_typeunsigned int

しかし

1, -211std::common_typeintですので、あなたがそれらを比較するとき、あなたは-211が最小として返されますです。 -211, 3と、また-211, -63を比較すると同じことが言えます。しかし、-211 89uを比較すると、std::common_typeunsigned intなので、-211は4294967085になります。したがって、89uは最小値です。もちろん、最小値でも最大値でもないので、ばかげています。整数昇進の犠牲者に過ぎない。あなたはすべての種類の間で均一にcommon_typeを使用したい場合は

は、その後、あなたはこのような何かがあります

template<class T, class U> 
std::common_type_t<T, U> 
variadic_min(const T& t, const U& u) 
{ 
    if (t < u) 
     std::cout << t << " less than " << u << std::endl; 
    else 
     std::cout << u << " less than " << t << std::endl; 
    return t < u ? t : u; 
} 

template<class First, class Second, class... Rest> 
std::common_type_t<First, Second, Rest...> 
variadic_min(const First& f, const Second& s, const Rest& ...t) 
{ 
    using ret_t = std::common_type_t<First, Second, Rest...>; 
    return variadic_min(variadic_min(static_cast<ret_t>(f), static_cast<ret_t>(s)), static_cast<ret_t>(t)...); 
} 

そして少なくともあなたは不可欠なプロモーションによって被害を受けていないが、すべてのタイプがunsigned intに変換されるので、出力は少なくとも予測可能です。すべての負の数が正の大きなものになるだろう:

出力:

1 less than 4294967085 
1 less than 3 
1 less than 4294967233 
1 less than 89 
Res: 1 

は、最終的にはより良いアプローチは、未署名のために目を光らせてするかもしれない/比較を締結し、その場合には、特別な何かをします。例えば。最初に符号付きの値を0にチェックし、それよりも小さい場合は、符号付きの値を大きな正の値にキャストするのではなく、その値を返します。

2

あなたは警告してコンパイルする場合は、あなたのミスが表示されます:

<source>:7:11: warning: returning reference to local temporary object [-Wreturn-stack-address] 
    return t < u ? t : u; 
      ^~~~~~~~~~~~~ 
<source>:15:17: note: in instantiation of function template specialization 'multi_type_min<float, unsigned int>' requested here 
    ((retVal= &multi_type_min(*retVal, t)), ...); 
       ^
<source>:28:14: note: in instantiation of function template specialization 'variadic_min<float, unsigned int, unsigned int, unsigned int>' requested here 
     return variadic_min(z, a,b,c); 
      ^
1 warning generated. 

であるように、あなたは、未定義の動作を持っています。 TUは同じ型ではない可能性があるので、それらの両方に対する共通の参照/ポインタを持つことはできません。

  1. multi_type_minには、値ではなく参照値を返します。
  2. retValをポインタではなく値にします。

Demo

#include <type_traits> 

template <typename T, typename U> 
const typename std::common_type<T, U>::type 
multi_type_min(const T& t, const U& u) 
{ 
    return t < u ? t : u; 
} 

template<typename First, typename ...T> 
typename std::common_type< First, T...>::type 
variadic_min(const First& f, const T& ...t) 
{ 
    typename std::common_type< First, T...>::type retVal = f; 
    ((retVal= multi_type_min(retVal, t)), ...); 

    return retVal; 
} 

int main() 
{ 
    unsigned int a=8, b= 2, c=4; 
    float z = 43.42f; 

    return variadic_min(z, a,b,c); 
} 

ここで別の実装だという一般的なタイプに基づいて、初期化子リストを作成した後std::minに委譲:

template<typename First, typename ...T> 
constexpr 
typename std::common_type<First, T...>::type 
common_min(const First& f, const T& ...t) { 
    std::initializer_list<typename std::common_type<First, T...>::type> ilist = { 
     static_cast<typename std::common_type<First, T...>::type>(f), 
     static_cast<typename std::common_type<First, T...>::type>(t)... 
    }; 
    return std::min(ilist); 
} 
+0

値はここでretvalにコピーされますが、そうではありませんか? –

+2

@AdamHunyadi:はい、そうです。それはやむを得ないことです。 – Cornstalks

+0

'' const ref''型を値型に変換するには 'common_type :: type(等)'を使います。 –

関連する問題