投稿された両方の回答は正しいですが (私は両方に賛成票を投じました)、将来これを見つけた人のために、もう少し詳しく説明したいと思います。
「友達」の意味
まず、「フレンド」は、クラス内の関数では異なる意味を持ちます。単なる関数宣言の場合は、指定された関数をクラスのフレンドとして宣言し、そのプライベート/保護されたメンバーへのアクセスを許可します。ただし、それが関数の実装である場合、その関数は (a) クラスのフレンドであり、(b) クラスのメンバーではなく、(c) 囲んでいる名前空間内からはアクセスできないことを意味します。すなわち。これは、引数依存ルックアップ(ADL)を介してのみアクセスできるグローバル関数になります。
たとえば、次のテスト コードを見てください。
#include <iostream>
#include <iterator>
namespace nsp
{
template <class element_type, class element_allocator_type = std::allocator<element_type> >
class test_container
{
private:
element_type numbers[50];
friend class iterator;
public:
class iterator : public std::iterator<std::bidirectional_iterator_tag, element_type>
{
private:
element_type *i;
template <class distance_type>
friend void advance(iterator &it, distance_type n);
friend typename std::iterator_traits<iterator>::difference_type distance(const iterator &first, const iterator &last)
{
return last.i - first.i;
}
public:
iterator(element_type &_i)
{
i = &(_i);
}
element_type & operator *()
{
return *i;
}
element_type & operator = (const iterator source)
{
i = source.i;
return *this;
}
bool operator != (const iterator rh)
{
return i != rh.i;
}
iterator & operator ++()
{
++i;
return *this;
}
iterator & operator --()
{
--i;
return *this;
}
};
iterator begin()
{
return iterator(numbers[0]);
}
iterator end()
{
return iterator(numbers[50]);
}
template <class distance_type>
friend void advance(iterator &it, distance_type n)
{
it.i += 2 * n;
}
};
}
int main(int argc, char **argv)
{
nsp::test_container<int> stuff;
int counter = 0;
for (nsp::test_container<int>::iterator it = stuff.begin(); it != stuff.end(); ++it)
{
*it = counter++;
}
nsp::test_container<int>::iterator it = stuff.begin(), it2 = stuff.begin();
using namespace std;
cout << *it << endl;
++it;
cout << *it << endl;
advance(it, 2);
cout << *it << endl;
std::advance(it, 2);
cout << *it << endl;
int distance_between = distance(it2, it);
cout << distance_between << endl;
cin.get();
return 0;
}
from withinmain()
がadvance()
呼び出されると、ADL が機能し、クラス イテレータのカスタム Advance が呼び出されます。ただし、nsp::advance()
またはnsp::test_container<int>::advance()
をstuff.advance()
試すと、コンパイル エラー (「一致する関数呼び出しがありません」) が発生します。
テンプレートの問題
テンプレート関数のオーバーロードに優先して非テンプレート関数のオーバーロードが呼び出されることは事実ですが、これは ADL の使用には関係ありません。関数がテンプレートであるか非テンプレートであるかに関係なく、特定の型の正しいオーバーロードが呼び出されます。さらに、advance()
具体的には距離型 (int、long int、long long int など) のテンプレート パラメーターが必要です。これをスキップすることはできません。コンパイラーがどの型から推論しようとしているかがわからないためです。 」であり、プログラマーがどのようなタイプをスローするかはわかりませんadvance()
。幸いなことに、カスタム アドバンスとは異なる名前空間にある部分的な特殊化について心配する必要はなく、上記の例に示すように、ハードコードされたイテレータ型を使用してstd::advance()
独自のものを簡単に実装できます。advance()
これは、イテレーター自体がテンプレートであり、パラメーターを受け取る場合でも機能します。パラメーターを事前テンプレートに含め、テンプレート化されたイテレーター型をそのようにハードコードするだけです。例えば。:
template <class element_type, class distance_type>
friend void advance(iterator<element_type>, distance_type distance);
その他のテンプレートの問題 (補足)
これは特に実装に関連するものではありませんがadvance()
、一般的なクラス フレンド関数の実装に関連しています。上記の例では、非テンプレート関数distance()
を iterator クラス内に直接実装しているのに気付くでしょう。一方、advance()
テンプレート化された関数は iterator クラスの外側で test_container クラス内でフレンドとして宣言されています。これはポイントを説明するためのものです。
コンパイラがエラーをスローするため、クラスがテンプレート (またはテンプレートの一部) である場合、非テンプレート フレンド関数をフレンドであるクラスの外部に実装することはできません。ただし、テンプレート化された関数は、フレンド クラスに含まれる定義のみを使用して、クラスの外部で宣言advance()
できます。このadvance()
関数は、フレンド クラス内で直接実装することもできますが、この点を説明するために実装しないことにしました。
テンプレート フレンド関数パラメーターのシャドーイング
これは上記の例とは関係ありませんが、プログラマーがテンプレート フレンド関数に足を踏み入れる際の落とし穴になる可能性があります。テンプレート クラスと、そのクラスで動作するフレンド関数がある場合、関数定義とクラスでテンプレート パラメーターを指定する必要があることは明らかです。例えば:
template <class element_type, class element_allocator_type = std::allocator<element_type> >
class test_container
{
private:
element_type numbers[50];
public:
template <class element_type, class element_allocator_type>
friend void do_stuff(test_container<element_type, element_allocator_type> &container)
{
numbers[1] = 5; // or whatever
}
};
ただし、コンパイラは「element_type」と「element_allocator_type」に同じ名前を使用すると、test_container の定義で最初に使用されたテンプレート パラメーターの再定義であると見なし、エラーをスローするため、上記は機能しません。したがって、これらには別の名前を使用する必要があります。すなわち。これは機能します:
template <class element_type, class element_allocator_type = std::allocator<element_type> >
class test_container
{
private:
element_type numbers[50];
public:
template <class c_element_type, class c_element_allocator_type>
friend void do_stuff(test_container<c_element_type, c_element_allocator_type> &container)
{
numbers[1] = 5; // or whatever
}
};
それだけです-これに出くわした人がそれを利用できることを願っています-この情報のほとんどは、何らかの形、形、または形式でstackoverflow全体に広がっていますが、初心者にとってはそれらをまとめることが重要です.
[更新:] 上記のすべてを使用しても、「正しい」にもかかわらず、ADL を正しい関数に正しく解決するにはまだ十分ではない可能性があります。これは、clang、Microsoft Visual Studio 2010 ~ 2013、およびその他のバージョンでは、複雑なテンプレートで ADL を解決するのが困難であり、いずれにせよクラッシュまたはエラーをスローする可能性があるためです。この場合、イテレータ クラスに関連付けられている標準のコンテナ関数に単純に頼るのが賢明です。