17

私はデフォルト値で1つのパラメータをとる関数を持っています。今私はまた、それはパラメータの可変数を取って、いくつかの他の関数に転送したい。関数のパラメータはデフォルト値で最後にする必要があります。なぜなら、variadicパックの後にそのパラメータを置くことができますし、関数を呼び出すときにコンパイラがそれを供給しているかどうかを検出しますか?デフォルト値のC++バリデーションテンプレート関数パラメータ

(パックには最後の1つのパラメータの型が含まれていないと仮定します)必要に応じて、その型は一般的にユーザーに知られていないと思われます。インターフェイスはとにかく....)

template <class... Args> 
void func (Args&&... args, SomeSpecialType num = fromNum(5)) 
{ 
} 

答えて

16

いいえ、パックは最後にする必要があります。

しかし、あなたはそれを偽造することができます。パックの最後のタイプが何であるかを検出することができます。 SomeSpecialTypeの場合、funcを実行できます。 SomeSpecialTypeでなければ、引数を転送し、fromNum(5)を追加して再帰的に自分自身を呼び出すことができます。

SFINAE技術を使用してコンパイル時(つまり、異なるオーバーロード)でこのチェックを行うことができます。しかし、おそらく、ランタイムチェックは一定の過負荷で一定であるため、ほぼ確実に最適化され、SFINAEは軽く使用しないでください。

これはあなたに必要な署名を与えませんが、あなたに必要な動作を与えます。コメントに意図された署名を説明する必要があります。

このような何かを、あなたがタイプミス等を除去した後:

// extract the last type in a pack. The last type in a pack with no elements is 
// not a type: 
template<typename... Ts> 
struct last_type {}; 
template<typename T0> 
struct last_type<T0> { 
    typedef T0 type; 
}; 
template<typename T0, typename T1, typename... Ts> 
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...> {}; 

// using aliases, because typename spam sucks: 
template<typename Ts...> 
using LastType = typename last_type<Ts...>::type; 
template<bool b, typename T=void> 
using EnableIf = typename std::enable_if<b, T>::type; 
template<typename T> 
using Decay = typename std::decay<T>::type; 

// the case where the last argument is SomeSpecialType: 
template< 
    typename... Args, 
    typename=EnableIf< 
    std::is_same< 
     Decay<LastType<Args...>>, 
     SomeSpecialType 
    >::value 
    > 
void func(Args&&... args) { 
    // code 
} 

// the case where there is no SomeSpecialType last:  
template< 
    typename... Args, 
    typename=EnableIf< 
    !std::is_same< 
     typename std::decay<LastType<Args...>>::type, 
     SomeSpecialType 
    >::value 
    > 
void func(Args&&... args) { 
    func(std::forward<Args>(args)..., std::move(static_cast<SomeSpecialType>(fromNum(5)))); 
} 

// the 0-arg case, because both of the above require that there be an actual 
// last type: 
void func() { 
    func(std::move(static_cast<SomeSpecialType>(fromNum(5)))); 
} 

またはそのような多くの何か。

+0

これは回避策のようですが、それは別の署名ですが、同じ動作です...わかります。実際に私は将来このパラメータを削除するつもりだったので、努力する価値はないかもしれません(また、署名が混乱するかもしれません)。簡単な例を教えていただけますか? – cfa45ca55111016ee9269f0a52e771

+0

@ fr33domlover私はデザインをスケッチしました。コンパイルされておらず、デバッグはされていませんが、基本はそこにあるはずです。 – Yakk

+0

ただ一つのパラメータを削除するだけではない場合、私は試してみます。それは複雑に見え、署名は保持されないので、トラブルの価値がないかもしれません...とにかく感謝 – cfa45ca55111016ee9269f0a52e771

3

もう1つの方法は、可変引数をタプルで渡すことです。

長所:インターフェイスははるかに簡単です。オーバーロードしてデフォルト値の引数を追加するのは簡単です。

短所:呼び出し側はstd::make_tupleまたはstd::forward_as_tupleコールで引数を手動でラップする必要があります。また、この機能を実装するには、恐らくstd::index_sequenceのトリックに頼らざるを得ません。

関連する問題