C++ как различать шаблон для контейнера и собственный тип
у меня следующая проблема:
template<class T>
void set(std::string path, const T data)
{
stringstream ss;
ss << data << std::endl;
write(path, ss.str();
}
template<class T>
void set(std::string path, const T data)
{
std::stringstream ss;
for(typename T::const_iterator it = data.begin(); it < data.end(); ++it)
{
ss << *it;
if(it < data.end() -1 )
ss << ", ";
}
ss << std::endl;
write(path, ss.str());
}
Я получаю следующую ошибку:
error: ‘template<class T> void myclass::set(std::string, T)’ cannot be overloaded
error: with ‘template<class T> void myclass::set(std::string, T)’
есть ли способ различать типы контейнеров и другие типы в шаблонах?
4 ответов
В C++03 вы можете сделать это с помощью небольшого количества SFINAE, чтобы выборочно включить разные версии функции для разных типов:
#include <boost/type_traits.hpp>
#include <sstream>
#include <iostream>
#include <vector>
using namespace std;
template<class T>
void set(typename boost::enable_if<boost::is_pod<T>, std::string>::type path, const T data)
{
std::cout << "POD" << std::endl;
stringstream ss;
ss << data << std::endl;
}
template<class T>
void set(typename boost::disable_if<boost::is_pod<T>, std::string>::type path, const T data)
{
std::cout << "Non-POD" << std::endl;
std::stringstream ss;
for(typename T::const_iterator it = data.begin(); it < data.end(); ++it)
{
ss << *it;
if(it < data.end() -1 )
ss << ", ";
}
ss << std::endl;
}
int main() {
int i;
float f;
std::vector<int> v;
set("", v);
set("", i);
set("", f);
}
я использовал boost для удобства здесь, но вы можете свернуть свой собственный, если boost не вариант или использовать C++11 вместо этого.
is_pod
не совсем то, что вы хотите на самом деле, вы, вероятно, хотите is_container
черта, но это не так тривиально, вам нужно будет сделать свою собственную черту и is_pod
дает хорошее приближение как использовать признаки для выборочного включения функций в качестве простого ответа.
использовать черту:
#include <type_traits>
template <typename T>
typename std::enable_if<is_container<T>::value>::type
set (std::string const & path, T const & container)
{
// for (auto const & x : container) // ...
}
template <typename T>
typename std::enable_if<!is_container<T>::value>::type
set (std::string const & path, T const & data)
{
std::ostringstream oss;
oss << data;
write(path, oss.str());
}
вы можете найти подходящую черту в довольно принтера код.
вы можете попробовать ошибку подстановки не является ошибкой (SFINAE) методы здесь.
во-первых, вам нужна функция, чтобы определить, является ли тип элемента итератора...
template <typename T>
struct Has_Iterator
{
template <typename>
static char test(...);
template <typename U>
static int test(typename U::const_iterator*);
static const bool result = sizeof test<T>(0) != sizeof(char);
};
в приведенном выше коде, стандарт C++ требует test(typename U::const_iterator*)
для использования в предпочтении к расплывчатому "...- параметры совпадают, пока U
is фактически структура / класс с const_iterator
тип члена. В противном случае у вас есть "сбой замены", который не является фатальным ошибка остановки компиляции (следовательно, SFINAE), и попытка найти соответствующую функцию удовлетворяется test(...)
. Поскольку возвращаемые типы двух функций различаются, то sizeof
оператор может проверить, какой из них был подобран, задание result
логическое надлежащим образом.
затем вы можете пересылать запросы на печать вещей в специализации шаблонов, которые их поддерживают...
template <typename T>
void print(const T& data)
{
printer<Has_Iterator<T>::result, T>()(data);
}
// general case handles types having iterators...
template <bool Has_It, typename T>
struct printer
{
void operator()(const T& data)
{
for (typename T::const_iterator i = data.begin(); i != data.end(); ++i)
std::cout << *i << ' ';
std::cout << '\n';
}
};
// specialisation for types lacking iterators...
template <typename T>
struct printer<false, T>
{
void operator()(const T& data)
{
std::cout << data << '\n';
}
};
как писали мои предшественники, вы должны использовать какую-то черту. Вы, вероятно, должны использовать Boost для этого, но если вы не хотите, вы можете просто использовать что-то вроде этого (http://ideone.com/7mAiB ):
template <typename T>
struct has_const_iterator {
typedef char yes[1];
typedef char no[2];
template <typename C> static yes& test(typename C::const_iterator*);
template <typename> static no& test(...);
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
template <bool> class bool2class {};
template <class T>
void set_inner(const std::string &path, T & var, bool2class<false> *) {
// T is probably not STL container
}
template <class T>
void set_inner(const std::string &path, T & var, bool2class<true> *) {
// T is STL container
}
template <class T>
void set(const std::string &path, T &var) {
set_inner(path, var, (bool2class<has_const_iterator<T>::value>*)0);
}
это непростая задача отличить контейнер от простого массива, поэтому я использовал здесь проверку, имеет ли тип const_iterator
. Вероятно, вы также должны проверить, имеет ли он begin()
, end()
и другие вещи, которые вы будете использовать в будущем код.