以前にこの回答を読んだことがある場合は、下の ADL バージョンまでスクロールしてください。かなり改善されました。
まず、ほとんど機能する短くて甘いバージョン:
#include <iostream>
#include <type_traits>
template<typename T, typename Iterator, typename=void>
struct is_iterator_of_type: std::false_type {};
template<typename T, typename Iterator>
struct is_iterator_of_type<
T,
Iterator,
typename std::enable_if<
std::is_same<
T,
typename std::iterator_traits< Iterator >::value_type
>::value
>::type
>: std::true_type {};
template<typename Container>
auto operator<<( std::ostream& stream, Container const& c ) ->
typename std::enable_if< is_iterator_of_type<int, typename Container::iterator>::value, std::ostream& >::type
{
return stream << "int container\n";
}
template<typename Container>
auto operator<<( std::ostream& stream, Container const& c ) ->
typename std::enable_if< is_iterator_of_type<double, typename Container::iterator>::value, std::ostream& >::type
{
return stream << "double container\n";
}
これは、似ているものint
やdouble
明確なオーバーロードを持つコンテナを検出するだけです。の実装を変更することをお勧めしoperator<<
ます。;)
より適切なルート (@Xeo に感謝) は、この adl-hack です。begin
andend
からをインポートする補助名前空間を作成し、std
引数に依存する and を検索するいくつかのテンプレート関数を作成しbegin
(end
よりstd
厳密にバインドされたバージョンがない場合はバージョンを確認します)、これらのaux::adl_begin
関数を使用して、私たちが何であるかを判断します。渡されたものは、X 上のコンテナーとして扱うことができます。
#include <iostream>
#include <vector>
#include <type_traits>
#include <iterator>
#include <set>
template<typename T, typename Iterator, typename=void>
struct is_iterator_of_type: std::false_type {};
template<typename T, typename Iterator>
struct is_iterator_of_type<
T,
Iterator,
typename std::enable_if<
std::is_same<
T,
typename std::iterator_traits< Iterator >::value_type
>::value
>::type
>: std::true_type {};
namespace aux {
using std::begin;
using std::end;
template<class T>
auto adl_begin(T&& v) -> decltype(begin(std::forward<T>(v))); // no implementation
template<class T>
auto adl_end(T&& v) -> decltype(end(std::forward<T>(v))); // no implementation
}
template<typename T, typename Container, typename=void>
struct is_container_of_type: std::false_type {};
template<typename T, typename Container>
struct is_container_of_type<
T,
Container,
typename std::enable_if<
// we only want this to be used if we iterable over doubles:
is_iterator_of_type<
T,
decltype(void(aux::adl_begin(*(Container*)nullptr)), aux::adl_end(*(Container*)nullptr)) // ensure being and end work as bonus
>::value
>::type
>: std::true_type
{};
template<class Ch, class Tr, class Container>
auto operator<<( std::basic_ostream<Ch,Tr>& stream, Container const& c ) ->
typename std::enable_if<
is_container_of_type<double, Container>::value,
decltype(stream)
>::type
{
stream << "'double' container: [ ";
for(auto&& e:c)
stream << e << " ";
return stream << "]";
}
int main() {
std::cout << std::vector<double>{1,2,3} << "\n";
std::cout << std::set<double>{3.14,2.7,-10} << "\n";
double array[] = {2.5, 3.14, 5.0};
std::cout << array << "\n";
}
double
これにより、 s の配列が のコンテナとしてカウントされるだけでなくdouble
、その名前空間でコンテナを引数として取る double のイテレータを返す関数を定義する場合も同様に機能しますbegin
。end
これはfor(auto&& i:container)
ルックアップがどのように機能するか (完全に? 適度に?) と一致するため、「コンテナー」の有効な定義です。
ただし、これらの装飾を追加するにつれて、現在使用しているすべての C++11 機能を備えているコンパイラは少なくなっていることに注意してください。上記はgcc 4.6でコンパイルされると思いますが、gcc 4.5.*ではコンパイルされません。
...
そして、これが元の短いコードで、その周りにいくつかのテスト フレームワークがあります: (コンパイラがスローする場合に便利です。以下でどこが間違っているかを確認できます)。
#include <iostream>
#include <type_traits>
#include <vector>
#include <iostream>
#include <set>
template<typename T, typename Iterator, typename=void>
struct is_iterator_of_type: std::false_type {};
template<typename T, typename Iterator>
struct is_iterator_of_type<
T,
Iterator,
typename std::enable_if<
std::is_same<
T,
typename std::iterator_traits< Iterator >::value_type
>::value
>::type
>: std::true_type {};
void test1() {
std::cout << is_iterator_of_type<int, std::vector<int>::iterator>::value << "\n";
}
template<typename T, typename Container>
auto foo(Container const&) -> typename std::enable_if< is_iterator_of_type<T, typename Container::iterator>::value >::type
{
std::cout << "Container of int\n";
}
template<typename T>
void foo(...)
{
std::cout << "No match\n";
}
void test2() {
std::vector<int> test;
foo<int>(test);
foo<int>(test.begin());
foo<int>(std::set<int>());
}
template<typename Container>
auto operator<<( std::ostream& stream, Container const& c ) ->
typename std::enable_if< is_iterator_of_type<int, typename Container::iterator>::value, std::ostream& >::type
{
return stream << "int container\n";
}
void test3() {
std::vector<int> test;
std::cout << test;
std::set<int> bar;
std::cout << bar;
}
template<typename Container>
auto operator<<( std::ostream& stream, Container const& c ) ->
typename std::enable_if< is_iterator_of_type<double, typename Container::iterator>::value, std::ostream& >::type
{
return stream << "double container\n";
}
void test4() {
std::vector<int> test;
std::cout << test;
std::set<int> bar;
std::cout << bar;
std::vector<double> dtest;
std::cout << dtest;
}
void test5() {
std::vector<bool> test;
// does not compile (naturally):
// std::cout << test;
}
template<typename Container>
auto operator<<( std::ostream& stream, Container const& c ) ->
typename std::enable_if< is_iterator_of_type<bool, typename Container::iterator>::value, std::ostream& >::type
{
return stream << "bool container\n";
}
void test6() {
std::vector<bool> test;
// now compiles:
std::cout << test;
}
int main() {
test1();
test2();
test3();
test4();
test5();
test6();
}
上記の約半分はボイラープレートのテストです。is_iterator_of_type
テンプレートとオーバーoperator<<
ロードはあなたが望むものです。
type のコンテナはT
、.typedefiterator
を持つクラスでvalue_type
あると想定していますT
。std
これは、すべてのコンテナーとほとんどのカスタム コンテナーをカバーします。
実行へのリンク: http://ideone.com/lMUF4i -- 一部のコンパイラは完全な C++11 SFINAE をサポートしておらず、動作させるために tomfoolery が必要になる場合があることに注意してください。
テストケースは、コンパイラがこれらの手法に対してどのレベルのサポートを提供しているかを誰かが確認するのに役立つように残されています。