類似しているが異なるテンプレート シグネチャを持つ C++ の関数をオーバーロードすることは可能ですか。
max() 関数について考えてみましょう。max()
次の 3 つのオプションのペアを受け取ることができる関数が必要な場合: int
、*int
範囲、int
反復子の範囲。
STL には、この機能が 2 つの機能に分割されています。
では、C++ でこの動作を実現できますか、または実現できませんか? もしそうなら、どのようにしますか?
編集:これは正常に作成max(vec.begin(), vec.end());
およびmax(vec[0], vec[1]);
コンパイルされ、2 つの渡されたオブジェクトの範囲の最大値と最大値を見つけます。個人的には、そうでなければ STL で 2 つの機能を組み合わせるのは不可能だと思いますが、実行できないことを確認します。
現時点では、オーバーロードは制限されていないため、あいまいです。コンパイラーは、最初のものがイテレーターに使用されるべきではないことをどのように知っているのですか?
答えは簡単です、あなたはそれを伝えなければなりません。幸いなことに、標準では、std::iterator_traits
渡されたイテレータ タイプに関するあらゆる種類の情報をクエリできるものが定義されています (イテレータである限り)。
ここでの最後の点が重要です:IFがstd::iterator_tags<T>
入れ子になったのtypedefを持っていないiterator_category
、そしてT
イテレータではありません。したがって、メンバー検出を使用void_t
して、指定された型がイテレーターであるかどうかを確認するカスタム特性を定義できます。
コードでは、次のようになります。
// C++17 void_t
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct is_iterator : std::false_type {
};
template <typename T>
struct is_iterator<T, void_t<typename std::iterator_traits<T>::iterator_category>> : std::true_type {
};
template <typename T>
constexpr auto is_iterator_v = is_iterator<T>::value;
ここではis_iterator_v
、便宜上constexpr 変数も定義しています。
この特性を と組み合わせて使用しstd::enable_if
て、型T
がイテレータであるかどうかに基づいて、以前はあいまいであった 2 つのオーバーロードのいずれかを選択的に無効にすることができます。これまでに、単純にテンプレート宣言を置き換えるtemplate <typename T>
ことにより、template <typename T, std::enable_if_t<is_iterator_v<T>, int> = 0>
およびtemplate <typename T, std::enable_if<!is_iterator_v<T>, int> = 0>
それぞれ。
すべてをまとめると、コードは次のようになります。
// C++17 void_t
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct is_iterator : std::false_type {
};
template <typename T>
struct is_iterator<T, void_t<typename std::iterator_traits<T>::iterator_category>> : std::true_type {
};
template <typename T>
constexpr auto is_iterator_v = is_iterator<T>::value;
template<typename T, std::enable_if_t<!is_iterator_v<T>, int> = 0>
constexpr T maximum(T a, T b)
{
return (a > b) ? a : b;
}
template<typename Iter, std::enable_if_t<is_iterator_v<Iter>, int> = 0>
constexpr Iter maximum(Iter begin, Iter end)
{
if(begin == end) {
return end;
}
auto max = begin;
for (; begin != end; ++begin) {
if(*begin > *max) {
max = begin;
}
}
return max;
}
wandbox で実際の例を見つけることもできます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加