SFINAE编译器麻烦

fre*_*low 7 c++ containers stl sfinae visual-studio-2008

我的以下代码应该检测是否Tbeginend方法:

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)) };
};
Run Code Online (Sandbox Code Playgroud)

这是一些测试代码:

#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';
}
Run Code Online (Sandbox Code Playgroud)

在g ++ 4.5.1上,输出是1 1 1 1.但是,在Visual Studio 2008上,输出是1 1 0 0.我做错了什么,或者这只是一个VS 2008错误?任何人都可以测试不同的编译器吗?谢谢!

Edw*_*nge 12

所以,这是我如何调试这些东西.

首先,注释掉负面的替代方案,这样你就会得到一个错误,而不仅仅是一个不匹配.接下来,尝试使用其中一个不起作用的项来实例化您放入函数的类型.

在这一步,我能够实例化你的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;
};
Run Code Online (Sandbox Code Playgroud)

仍然无法正常工作,但至少我收到了一条有用的错误信息:

error C2440: 'specialization' : cannot convert from 'overloaded-function' to 'std::_Tree_const_iterator<_Mytree> (__thiscall std::set<_Kty>::* )(void) const'

这让我知道&U::endVS(ANY编译器)不能告诉我想要哪个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;
Run Code Online (Sandbox Code Playgroud)

将它们全部重新组合在一起并运行您的测试程序......使用VS2010取得成功.你可能会发现static_cast实际上就是你所需要的,但是我把它留给你找出来.

我想现在真正的问题是,哪个编译器是对的?我的赌注是一致的:g ++.指向智者:永远不要假设我当时做了什么.

编辑:Jeesh ...... 你错了!

更正版本:

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';
}
Run Code Online (Sandbox Code Playgroud)

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

  • @sbi:SFINAE意味着"替换失败**不是**错误".如果`T`没有`begin`和`end`方法,那么`is_container <T> :: value`应该仍然**编译**(并且产生false).这正是我的代码在g ++中正确执行的操作. (3认同)

MSa*_*ers 5

你为什么要这么努力?如果你想检查是否U::begin()存在,为什么不试试呢?

template <typename T>
struct is_container
{
    template <typename U> static char test(U* u,
       typename U::const_iterator b = ((U*)0)->begin(),
       typename U::const_iterator e = ((U*)0)->end());
    template <typename U> static long test(...);

    enum { value = (1 == sizeof test<T>(0)) };
};
Run Code Online (Sandbox Code Playgroud)

除了检查是否存在等U::begin()U::end(),这也检查是否返回的东西,可以转换为const_iterator.它还避免了Stephan T. Lavavej通过使用必须支持的调用表达式突出显示的陷阱,而不是假定特定的签名.

[编辑]抱歉,这依赖于VC10的模板实例化.更好的办法(放所有脑干检查的参数类型,参与过载):

template <typename T> struct is_container
{
    // Is.
    template <typename U>
    static char test(U* u, 
                     int (*b)[sizeof(typename U::const_iterator()==((U*)0)->begin())] = 0,
                     int (*e)[sizeof(typename U::const_iterator()==((U*)0)->end())] = 0);
    // Is not.
    template <typename U> static long test(...);

    enum { value = (1 == sizeof test<T>(0)) };
};
Run Code Online (Sandbox Code Playgroud)


fre*_*low 1

Stephan T. Lavavej说道

请注意,从技术上讲,禁止获取标准库成员函数的地址。(它们可以被重载,从而产生&foo::bar歧义,并且它们可以有额外的默认参数,从而阻止通过 消除歧义的尝试static_cast。)

所以我想我将使用更简单的版本,仅检查嵌套const_iterator类型。