2016-06-19 1 views
1

私は、次のコードスニペットを持っている:イニシャライザのリストはパラメータの順序によって変わってきますか?

#include <type_traits> 
#include <limits> 
#include <initializer_list> 
#include <cassert> 

template <typename F, typename... FIn> 
auto min_on(F f, const FIn&... v) -> typename std::common_type<FIn...>::type 
{ 
    using rettype = typename std::common_type<FIn...>::type; 
    rettype result = std::numeric_limits<rettype>::max(); 
    (void)std::initializer_list<int>{((f(v) < result) ? (result = static_cast<rettype>(v), 0) : 0)...}; 
    return result; 
} 

int main() 
{ 
    auto mod2 = [](int a) 
    { 
    return a % 2; 
    }; 

    assert(min_on(mod2, 2) == 2);  // PASSES as it should 
    assert(min_on(mod2, 3) == 3);  // PASSES as it should 
    assert(min_on(mod2, 2, 3) == 3); // PASSES but shouldn't - should be 2 
    assert(min_on(mod2, 2, 3) == 2); // FAILS but shouldn't - should be 2 
} 

テンプレート関数min_onの背後にある考え方は、それが表現f(v)の最小値を与えるように、それはそれvに渡されたパラメータのリストからパラメータxを返さなければならないことです。

私が観察している問題は、このコードに対し、上記のコードは失敗しますので、何とかstd::initializer_list内部パラメータの順序が重要であるということです。

assert(min_on(mod2, 3, 2) == 2); 

が動作します。ここで何が間違っているのでしょうか?

答えて

2

f(v) < resultの場合、機能は〜vに設定されます。 mod2fとすると、f(v)は0,1または-1になります。つまり、すべての値が1より大きい場合、f(v)は常にresultより小さくなるため、resultは最後にvに設定されます。陽性の数の真ん中に負数を入れてみてください。負の数は、どこに置いても常に結果になります。

assert(min_on(mod2, 2, 3, 4, -3, 7, 6, 5) == -3); 

おそらく、あなたの代わりにこれをしたい:

std::initializer_list<int>{((f(v) < f(result)) ? (result = static_cast<rettype>(v), 0) : 0)...}; 

差が代わりにf(v) < resultの、私はf(v) < f(result)をテストしています。ただし、関数はまだ一般的には正しくありませんが、f(std::numeric_limits<rettype>::max())が最大値であると仮定しているためです。 mod2の場合は動作します。しかし、このようなもので:

[](int a) { return -a; } 

明らかに間違っています。

template <typename F, typename FirstT, typename... FIn> 
auto min_on(F f, const FirstT& first, const FIn&... v) 
    -> typename std::common_type<FirstT, FIn...>::type 
{ 
    using rettype = typename std::common_type<FirstT, FIn...>::type; 
    rettype result = first; 
    auto result_trans = f(result); 
    auto v_trans = result_trans; 
    (void)std::initializer_list<int>{(
    (v_trans = f(v), v_trans < result_trans) 
     ? (result = static_cast<rettype>(v), result_trans = v_trans, 0) : 0)...}; 
    return result; 
} 
+0

がが最高の入力と最高出力の両方を保存する方が効率的でしょう:あなたはfに不要な呼び出しを避けたいなら、

template <typename F, typename FirstT, typename... FIn> auto min_on(F f, const FirstT& first, const FIn&... v) -> typename std::common_type<FirstT, FIn...>::type { using rettype = typename std::common_type<FirstT, FIn...>::type; rettype result = first; (void)std::initializer_list<int>{((f(v) < f(result)) ? (result = static_cast<rettype>(v), 0) : 0)...}; return result; } 

または:だから、多分あなたの代わりに、最初の引数を必要とする可能性がありますfへの繰り返しの呼び出しを避けるためです。 –

+0

完璧な答え!私はmore.Thanks多くを求めることができませんでしたBenjamin – Patryk

+0

これは、オプションを返すか、空のリストに失敗する必要があります。あなたの提案は、コンパイル時に空で失敗します。これは良いことです。 modedtコストでは、回答の索引を追跡し、答えを見つけた後に値に戻すことで、結果の型を繰り返しコピーすることを避けることができます(高価になる可能性があります)が、かなり進歩しています。 – Yakk

関連する問題