fre*_*low 7 c++ containers stl sfinae visual-studio-2008
我的以下代码应该检测是否T有begin和end方法:
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 ++应该失败了.
你为什么要这么努力?如果你想检查是否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)