Ответ Наваза , вероятно, является правильным решением для большинства случаев. Однако, если вы пытаетесь сделать это для многих экземпляров SomeContainerFromAThirdPartyLib<T>
классов и только нескольких функций (или неизвестного числа экземпляров, но с фиксированным числом функций, как это может случиться, если вы пишете свою собственную библиотеку), есть по-другому.
Предположим, нам дан следующий (неизменяемый) код:
namespace ThirdPartyLib
{
template <typename T>
class SomeContainerFromAThirdPartyLib
{
public:
typedef T ValueType; // not value_type!
// no difference_type
class iterator
{
public:
typedef T ValueType; // not value_type!
// no difference_type
// obviously this is not how these would actually be implemented
int operator != (const iterator& rhs) { return 0; }
iterator& operator ++ () { return *this; }
T operator * () { return T(); }
};
// obviously this is not how these would actually be implemented
iterator begin() { return iterator(); }
iterator end() { return iterator(); }
};
}
Мы определяем шаблон класса адаптера, содержащий необходимые typedef
s для iterator_traits
, и специализируем его, чтобы избежать проблем с указателями:
namespace MyLib
{
template <typename T>
class iterator_adapter : public T
{
public:
// replace the following with the appropriate types for the third party iterator
typedef typename T::ValueType value_type;
typedef std::ptrdiff_t difference_type;
typedef typename T::ValueType* pointer;
typedef typename T::ValueType& reference;
typedef std::input_iterator_tag iterator_category;
explicit iterator_adapter(T t) : T(t) {}
};
template <typename T>
class iterator_adapter<T*>
{
};
}
Затем для каждой функции, которую мы хотим вызвать с помощью SomeContainerFromAThirdPartyLib::iterator
, мы определяем перегрузку и используем SFINAE:
template <typename iter>
typename MyLib::iterator_adapter<iter>::difference_type
count(iter begin, iter end, const typename iter::ValueType& val)
{
cout << "[in adapter version of count]";
return std::count(MyLib::iterator_adapter<iter>(begin), MyLib::iterator_adapter<iter>(end), val);
}
Затем мы можем использовать его следующим образом:
int main()
{
char a[] = "Hello, world";
cout << "a=" << a << endl;
cout << "count(a, a + sizeof(a), 'l')=" << count(a, a + sizeof(a), 'l') << endl;
ThirdPartyLib::SomeContainerFromAThirdPartyLib<int> container;
cout << "count(container.begin(), container.end(), 0)=";
cout << count(container.begin(), container.end(), 0) << std;
return 0;
}
Вы можете найти работающий пример с необходимыми include
с и using
с при http://ideone.com/gJyGxU. Выход:
a=Hello, world
count(a, a + sizeof(a), 'l')=3
count(container.begin(), container.end(), 0)=[in adapter version of count]0
К сожалению, есть предостережения:
- Как я уже говорил, необходимо определить перегрузку для каждой функции, которую вы планируете поддерживать (
find
, sort
и так далее). Это очевидно не будет работать для функций в algorithm
, которые еще не были определены.
- Если не оптимизирован, могут быть небольшие потери производительности во время выполнения.
- Возможны проблемы с областями видимости.
Что касается последнего, вопрос в том, в какое пространство имен поместить перегрузку (и как вызвать версию std
). В идеале это должно быть в ThirdPartyLib
, чтобы его можно было найти с помощью аргументно-зависимого поиска, но я предположил, что мы не можем это изменить. Следующий лучший вариант - MyLib
, но затем вызов должен быть квалифицирован или перед using
. В любом случае конечный пользователь должен либо использовать using std::count;
, либо быть осторожным в отношении того, какие вызовы подпадают под std::
, поскольку, если std::count
ошибочно используется с SomeContainerFromAThirdPartyLib::iterator
, он, очевидно, потерпит неудачу (вся причина этого упражнения ).
Альтернативой, которую я не предлагаю , но для полной полноты изложения, было бы поместить ее непосредственно в пространство имен std
. Это приведет к неопределенному поведению; хотя это может работать для вас, в стандарте нет ничего, что гарантировало бы это. Если бы мы специализировали count
вместо его перегрузки, это было бы законно.