C++如何区分容器模板和本机类型

C++ how to differentiate between template for container and native type

本文关键字:本机 类型 何区 C++      更新时间:2023-10-16

我有以下问题:

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)’

有没有办法区分容器类型和模板中的其他类型?

使用特征:

#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());
}

你可以在漂亮的打印机代码中找到合适的特征。

在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很好地近似了如何使用特征来选择性地启用函数作为简单的答案。

您可以在此处尝试替换失败不是错误(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实际上是一个具有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()和其他您将在未来代码中使用的东西。