|
|
 |
 |
 |
 |
SFINAE for operator->
Hello all, Is there a way to determine whether a particular type supports the -> operator at compile time? I'm trying to write a template function (or a series of overloads) that will yield the raw pointer at "the end of the arrow". For types that support the operator, I have a reasonable solution, but I'd like to have a null pointer returned if the operator isn't supported by a particular object. So far I have: //----begin---- #include <memory> #include <iostream> template<typename Ptr, typename T> Ptr pointer_from_arrowable(T *p) { return p; } template<typename Ptr, typename Arrowable> Ptr pointer_from_arrowable(Arrowable &a) { return pointer_from_arrowable<Ptr>(a.operator->()); }
int main() { std::auto_ptr<int> ap(new int(5)); int *p = pointer_from_arrowable<int *>(ap); std::cout << *p << '\n'; // 5 return 0; }
//----end---- I thought I might be able to use SFINAE to help the compiler choose an overload that returns 0 when no operator-> exists, but I end up with errors (MSVC8 and MinGW g++ 3.4.5) about ambiguous overloads (see end of post): //----begin---- #include <memory> #include <iostream> template<typename T> struct mfn_pointer { typedef void (T::*type)(); }; template<typename Ptr, typename T> Ptr pointer_from_arrowable(T *p) { return p; } template<typename Ptr, typename Arrowable> Ptr pointer_from_arrowable( Arrowable &a, typename mfn_pointer<Arrowable>::type sfinae = (typename mfn_pointer<Arrowable>::type)&Arrowable::operator-> ) { return pointer_from_arrowable<Ptr>(a.operator->()); }
template<typename Ptr, typename Arrowable> Ptr pointer_from_arrowable(Arrowable &a) { return 0; } int main() { std::auto_ptr<int> ap(new int(5)); int *p = pointer_from_arrowable<int *>(ap); std::cout << *p << '\n'; // 5 return 0; }
//----end---- So is there any way to do what I want? Here are the compiler errors I got when I attempted the SFINAE code: MinGW g++ 3.4.5: -------------------------------------------------- g++ -o arrow.o -c arrow.cpp -ggdb3 -Wall -Wextra -pedantic -ansi -O0 - Wswitch -D _GLIBCXX_DEBUG arrow.cpp: In function `int main()': arrow.cpp:26: error: call of overloaded `pointer_from_arrowable(std::auto_ptr<int>&)' is ambiguous arrow.cpp:16: note: candidates are: Ptr pointer_from_arrowable(Arrowable&, typename mfn_pointer<Arrowable>::type) [with Ptr = int*, Arrowable = std::auto_ptr<int>] arrow.cpp:21: note: Ptr pointer_from_arrowable(Arrowable&) [with Ptr = int*, Arrowable = std::auto_ptr<int>] error: system call returned unexpected exit-code 1 -------------------------------------------------- MSVC 8: -------------------------------------------------- cl /Foarrow.obj /c arrow.cpp /nologo /Od /Zc:forScope,wchar_t /RTCc / GR /RTCs /Zi /wd4996 /D _CRT_SECURE_NO_DEPRECATE /RTCu /EHsc /MTd /W3 arrow.cpp arrow.cpp(26) : error C2668: 'pointer_from_arrowable' : ambiguous call to overloaded function arrow.cpp(21): could be 'Ptr pointer_from_arrowable<int*,std::auto_ptr<_Ty>>(Arrowable &)' with [ Ptr=int *, _Ty=int, Arrowable=std::auto_ptr<int> ] arrow.cpp(11): or 'Ptr pointer_from_arrowable<int*,std::auto_ptr<_Ty>>(Arrowable &,void (__thiscall std::auto_ptr<_Ty>::* )(void))' with [ Ptr=int *, _Ty=int, Arrowable=std::auto_ptr<int> ] while trying to match the argument list '(std::auto_ptr<_Ty>)' with [ _Ty=int ] error: system call returned unexpected exit-code 2 -------------------------------------------------- Kind regards, Edd
On 3 Jun, 19:54, e@nunswithguns.net wrote: > Hello all, > Is there a way to determine whether a particular type supports the -> > operator at compile time? > I'm trying to write a template function (or a series of overloads) > that will yield the raw pointer at "the end of the arrow". For types > that support the operator, I have a reasonable solution, but I'd like > to have a null pointer returned if the operator isn't supported by a > particular object.
<snipped> assuming T is something like template<class T> struct my_ptr { typedef T element_type; // like std::auto_ptr element_type * operator->() const; };
a possible solution would be (tried only on VC8) template<class T> struct has_arrowop { typedef char (&yes)[1]; typedef char (&no)[2]; template<class T> static yes foo(T *, typename T::element_type * (T::*pt)() const = &T::operator->); template<class T> static no foo(T const *, bool=false); // use the one your compiler supports //enum { value = sizeof(foo((T const *)0, true)) != sizeof(foo((T*)0)) }; static const bool value = sizeof(foo((T const *)0, true)) != sizeof(foo((T*)0)); };
#include <memory> #include <iostream> struct A {}; struct B { typedef int element_type; element_type * operator->() const; };
int main() { std::cout << "A : " << has_arrowop<A>::value << "\n"; std::cout << "B: " << has_arrowop<B >::value <<'\n'; std::cout << "std::auto_ptr<A> : " << has_arrowop<std::auto_ptr<A> >::value << "\n"; return 0; }
DS
Hi DS, Thanks for your reply. On 4 Jun, 12:14, dasjotre <dasjo@googlemail.com> wrote:
> On 3 Jun, 19:54, e @nunswithguns.net wrote:> Hello all, > > Is there a way to determine whether a particular type supports the -> > > operator at compile time? > a possible solution would be (tried only on VC8) > template<class T> > struct has_arrowop > { > typedef char (&yes)[1]; > typedef char (&no)[2]; > template<class T> > static yes foo(T *, typename T::element_type * (T::*pt)() const = > &T::operator->); > template<class T> > static no foo(T const *, bool=false); > // use the one your compiler supports > //enum { value = sizeof(foo((T const *)0, true)) != > sizeof(foo((T*)0)) }; > static const bool value = sizeof(foo((T const *)0, true)) != > sizeof(foo((T*)0)); > };
To keep MinGW g++ happy, I had to parameterise the foo() taking an element_type pointer by a type named U rather than T (which is the same as the class parameter type). I also made the other foo a non- template function, because I don't believe it needs the type parameter. But with or without these changes, it doesn't inherently check for operator->, unless I'm mistaken. Instead, a type is removed from overload resolution if it doesn't have a nested element_type. For example, the following type gives a compiler error when used with has_arrowop<>: struct X { typedef int element_type; };
In other words, the existence of operator-> is never checked unless an element_type exists. If it does exist, it is assumed that an operator- > is also supplied, which it may not be in cases such as X, above.
Am I interpreting your code correctly? Perhaps a little more context may help. I'm actually trying to deduce if a general iterator type supports operator->. For output iterators, I can assume not since even though it may be supplied by a given type of output iterator, it is not a requirement as far as the standard is concerned. For all other types of iterator (input, forward, bidirectional and random access) I thought I might be able to use your trick by replacing T::element_type with std::iterator_traits<T>::pointer, but this won't work as every iterator type is *required* to have such a type defined (I believe) and so sfinae gives me nothing here. Further more, I cannot just assume that all non-input iterators will supply an operator-> because that's not the case. They only need provide one if the expression (*p).m is well formed for an iterator p and some member m. std::istreambuf_iterator is an example of an input iterator that doesn't provide an operator->. So what I've done is assume that the following iterator's don't supply an operator: - output iterators - iterators whose value_type is of primitive type This may be sufficient, but I haven't convinced myself of that quite yet. If you have any more thoughts, *please* let me know :) Thanks again for your response! Edd
On 4 Jun, 22:37, e@nunswithguns.net wrote:
> Hi DS, > Thanks for your reply. > On 4 Jun, 12:14, dasjotre <dasjo@googlemail.com> wrote: > > On 3 Jun, 19:54, e@nunswithguns.net wrote:> Hello all, > > > Is there a way to determine whether a particular type supports the -> > > > operator at compile time? > > a possible solution would be (tried only on VC8) > > template<class T> > > struct has_arrowop > > { > > typedef char (&yes)[1]; > > typedef char (&no)[2]; > > template<class T> > > static yes foo(T *, typename T::element_type * (T::*pt)() const = > > &T::operator->); > > template<class T> > > static no foo(T const *, bool=false); > > // use the one your compiler supports > > //enum { value = sizeof(foo((T const *)0, true)) != > > sizeof(foo((T*)0)) }; > > static const bool value = sizeof(foo((T const *)0, true)) != > > sizeof(foo((T*)0)); > > }; > To keep MinGW g++ happy, I had to parameterise the foo() taking an > element_type pointer by a type named U rather than T (which is the > same as the class parameter type). I also made the other foo a non- > template function, because I don't believe it needs the type > parameter. > But with or without these changes, it doesn't inherently check for > operator->, unless I'm mistaken. Instead, a type is removed from > overload resolution if it doesn't have a nested element_type. > For example, the following type gives a compiler error when used with > has_arrowop<>: > struct X > { > typedef int element_type; > }; > In other words, the existence of operator-> is never checked unless an > element_type exists. If it does exist, it is assumed that an operator- > > is also supplied, which it may not be in cases such as X, above.
yes, the element_type is what kicks in SFINAE, not the existence of operator->. changing to template<class T, class U> static yes foo(T *, U * (T::*pt)() const = &T::operator->); doesn't help. the default parameter is never considered for SFINAE. the whole thing is useless :-( > Am I interpreting your code correctly?
better than me.
> Perhaps a little more context may help. > I'm actually trying to deduce if a general iterator type supports > operator->. > For output iterators, I can assume not since even though it may be > supplied by a given type of output iterator, it is not a requirement > as far as the standard is concerned. > For all other types of iterator (input, forward, bidirectional and > random access) I thought I might be able to use your trick by > replacing T::element_type with std::iterator_traits<T>::pointer, but > this won't work as every iterator type is *required* to have such a > type defined (I believe) and so sfinae gives me nothing here. > Further more, I cannot just assume that all non-input iterators will > supply an operator-> because that's not the case. They only need > provide one if the expression (*p).m is well formed for an iterator p > and some member m. std::istreambuf_iterator is an example of an input > iterator that doesn't provide an operator->. > So what I've done is assume that the following iterator's don't supply > an operator: > - output iterators > - iterators whose value_type is of primitive type > This may be sufficient, but I haven't convinced myself of that quite > yet.
I'm not very good with the 'chapter and verse' (take this with a pinch of salt) but I think that all iterators must provide operator *, input as rvalue and output as lvalue and all input iterators have to provide operator-> as well. the original SGI STL models all iterators, both input and output by their TrivialIterator model which requires operator-> > If you have any more thoughts, *please* let me know :)
check: http://www.boost.org/libs/iterator/doc/pointee.html and BOOST_MPL_HAS_XXX_TRAIT_DEF not exactly what you need but you might find it useful anyway. good luck ;) DS.
On 5 Jun, 12:42, dasjotre <dasjo@googlemail.com> wrote: > the original SGI STL models all iterators, both > input and output by their TrivialIterator model > which requires operator->
Actually, that is not correct. only input iterators are modelled by TrivialIterator.
On 5 Jun, 13:45, dasjotre <dasjo@googlemail.com> wrote: > On 5 Jun, 12:42, dasjotre <dasjo @googlemail.com> wrote: > > the original SGI STL models all iterators, both > > input and output by their TrivialIterator model > > which requires operator-> > Actually, that is not correct. only input iterators > are modelled by TrivialIterator.
( doh! ) and obviously only for iterators where (*it).member is well formed.
On 5 Jun, 13:59, dasjotre <dasjo@googlemail.com> wrote: > On 5 Jun, 13:45, dasjotre <dasjo @googlemail.com> wrote: > > On 5 Jun, 12:42, dasjotre <dasjo@googlemail.com> wrote: > > > the original SGI STL models all iterators, both > > > input and output by their TrivialIterator model > > > which requires operator-> > > Actually, that is not correct. only input iterators > > are modelled by TrivialIterator. > ( doh! ) > and obviously only for iterators where (*it).member > is well formed.
This is the catch! So the half-solution I'm currently using is to assume that an iterator doesn't have an operator-> if either: (a) it's a pointer (in which case -> can be applied directly) (b) std::iterator_traits<X>::iterator_category is std::output_iterator (c) std::iterator_traits<X>::value_type is a primitive type If an iterator's value type is primitive, operator-> doesn't have to exist because (*x).m doesn't make sense for any such iterator x in combination any member name, m. The case that I can't handle is if a non-output iterator's value type is non-primitive. Here I always assume that an operator-> does exist, but this may not be the case in some very rare and strange examples. I think those rare and strange cases are so rare and strange that in practice I don't have to worry. But I would still like a solution with 100% coverage :/ You can perhaps imagine some half-arsed class that acts like a primitive numerical type and whose interface is implemented entirely in terms of non-member operators. Edd
|
 |
 |
 |
 |
|