27

デフォルト値を持つ1つのパラメーターを取る関数があります。ここで、可変数のパラメーターを取り、それらを他の関数に転送することも必要です。デフォルト値を持つ関数パラメーターは最後でなければならないので、そのパラメーターを可変個パックの後に置くことはできますか?関数を呼び出すときに、コンパイラーはそれを提供しているかどうかを検出しますか?

(パックに最後のパラメーターの型が含まれていないと仮定します。必要に応じて、その型は一般にユーザーに知られているとは想定されていないため、必要に応じて想定できます。そうでない場合は、とにかく私のインターフェイスの間違った使用法と見なされます.. ..)

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

4 に答える 4

22

いいえ、パックは最後でなければなりません。

しかし、あなたはそれを偽造することができます。パックの最後のタイプが何であるかを検出できます。の場合はSomeSpecialType、関数を実行できます。そうでない場合は、引数を転送して追加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))) );
}

またはそれによく似たもの。

于 2013-02-11T02:48:01.470 に答える
10

別のアプローチは、タプルを介して可変引数を渡すことです。

template <class... Args>
void func (std::tuple<Args...> t, SomeSpecialType num = fromNum(5))
{
  // don't forget to move t when you use it for the last time
}

長所 : インターフェイスははるかにシンプルで、デフォルト値の引数のオーバーロードと追加は非常に簡単です。

std::make_tuple短所:呼び出し元は、または呼び出しで引数を手動でラップする必要がありstd::forward_as_tupleます。また、関数を実装するには、おそらくstd::index_sequenceトリックに頼る必要があります。

于 2015-09-28T07:32:51.143 に答える