我的以下代码应检测T是否具有beginend方法:

template <typename T>
struct is_container
{
    template <typename U, typename U::const_iterator (U::*)() const,
                          typename U::const_iterator (U::*)() const>
    struct sfinae {};

    template <typename U> static char test(sfinae<U, &U::begin, &U::end>*);
    template <typename U> static long test(...);

    enum { value = (1 == sizeof test<T>(0)) };
};

这是一些测试代码:
#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <map>

int main()
{
    std::cout << is_container<std::vector<std::string> >::value << ' ';
    std::cout << is_container<std::list<std::string> >::value << ' ';
    std::cout << is_container<std::set<std::string> >::value << ' ';
    std::cout << is_container<std::map<std::string, std::string> >::value << '\n';
}

在g++ 4.5.1上,输出为1 1 1 1。但是,在Visual Studio 2008上,输出为1 1 0 0。我做错了什么,还是仅仅是VS 2008错误?任何人都可以在其他编译器上进行测试吗?谢谢!

最佳答案

因此,这就是我调试这些东西的方法。

首先,注释掉否定的替代方案,以使您得到一个错误,而不仅仅是不匹配。
接下来,尝试使用不起作用的项之一实例化要在函数中放入的类型。

在这一步,我可以实例化您的sfinae对象,但是它仍然无法正常工作。 “这让我知道这是一个VS错误,因此问题是如何解决它。” -OBS

按照您的方式做,VS似乎在SFINAE上遇到了麻烦。当然可以!当您包裹sfinae对象时,它会更好地工作。我这样做是这样的:

template <typename U, typename it_t = typename U::const_iterator >
struct sfinae
{
  // typedef typename U::const_iterator it_t; - fails to compile with non-cont types.  Not sfinae
  template < typename U, typename IT, IT (U::*)() const, IT (U::*)() const >
  struct type_ {};

  typedef type_<U,it_t,&U::begin,&U::end> type;
};

仍然无法正常工作,但至少我收到了一条有用的错误消息:
error C2440: 'specialization' : cannot convert from 'overloaded-function' to 'std::_Tree_const_iterator<_Mytree> (__thiscall std::set<_Kty>::* )(void) const'
这让我知道&U::end不足以使VS(任何编译器)知道我想要哪一个end()。 static_cast修复了:
  typedef type_<U,it_t,static_cast<it_t (U::*)() const>(&U::begin),static_cast<it_t (U::*)() const>(&U::end)> type;

将它们放在一起,并在其上运行测试程序... VS2010成功。您可能会发现static_cast实际上是您所需要的,但是我留给您了解。

我想现在的真正问题是,哪个编译器正确?我的赌注是一致的:g++。指向明智的人:永远不要假设我当时所做的。

编辑:耶什...你错了!

更正的版本:
template <typename T>
struct is_container
{
    template <typename U, typename it_t = typename U::const_iterator >
    struct sfinae
    {
      //typedef typename U::const_iterator it_t;
      template < typename U, typename IT, IT (U::*)() const, IT (U::*)() const >
      struct type_ {};

      typedef type_<U,it_t,static_cast<it_t (U::*)() const>(&U::begin),static_cast<it_t (U::*)() const>(&U::end)> type;
    };

    template <typename U> static char test(typename sfinae<U>::type*);
    template <typename U> static long test(...);

    enum { value = (1 == sizeof test<T>(0)) };
};



#include <iostream>
#include <vector>
#include <list>
#include <set>
#include <map>

int main()
{
    std::cout << is_container<std::vector<std::string> >::value << ' ';
    std::cout << is_container<std::list<std::string> >::value << ' ';
    std::cout << is_container<std::set<std::string> >::value << ' ';
    std::cout << is_container<std::map<std::string, std::string> >::value << ' ';
    std::cout << is_container<bool>::value << '\n';
}

--
上面的调试是明智的,但是关于编译器的假设是错误的。由于我上面强调的原因,G++应该失败了。

10-08 08:23
查看更多