473,394 Members | 1,724 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,394 software developers and data experts.

SFINAE for operator->

edd
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<intap(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<intap(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>>(Ar rowable &)'
with
[
Ptr=int *,
_Ty=int,
Arrowable=std::auto_ptr<int>
]
arrow.cpp(11): or 'Ptr
pointer_from_arrowable<int*,std::auto_ptr<_Ty>>(Ar rowable &,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

Jun 3 '07 #1
6 2912
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
Jun 4 '07 #2
edd
Hi DS,

Thanks for your reply.

On 4 Jun, 12:14, dasjotre <dasjo...@googlemail.comwrote:
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

Jun 4 '07 #3
On 4 Jun, 22:37, e...@nunswithguns.net wrote:
Hi DS,

Thanks for your reply.

On 4 Jun, 12:14, dasjotre <dasjo...@googlemail.comwrote:
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.

Jun 5 '07 #4
On 5 Jun, 12:42, dasjotre <dasjo...@googlemail.comwrote:
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.
Jun 5 '07 #5
On 5 Jun, 13:45, dasjotre <dasjo...@googlemail.comwrote:
On 5 Jun, 12:42, dasjotre <dasjo...@googlemail.comwrote:
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.

Jun 5 '07 #6
edd
On 5 Jun, 13:59, dasjotre <dasjo...@googlemail.comwrote:
On 5 Jun, 13:45, dasjotre <dasjo...@googlemail.comwrote:
On 5 Jun, 12:42, dasjotre <dasjo...@googlemail.comwrote:
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

Jun 5 '07 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

8
by: Peter Collingbourne | last post by:
Hello I am trying to do some template metaprogramming involving enabling or disabling a method in a parameterised class based on some condition. I am trying to use the Boost enable_if mechanism...
3
by: justin.adam.miller | last post by:
I've been trying to use the sfinae principle in some code and have been getting many compiler errors. So I decided to try a very simplified version to see if I had the idea correct. Here's the...
2
by: Clark S. Cox III | last post by:
I'm writing a class that, depending on a template parameter, has constructors that take differing numbers of arguments. I initially thought that I could use SFINAE (via boost::enable_if_c) to...
4
by: kaalus | last post by:
Is it possible to use SFINAE to provide different implementations of some function depending on the fact that operator << is overloaded for some type? For example: template<class T> void...
1
by: Dilip | last post by:
I am a little confused about the difference between SFINAE and unambiguous overload resolution set. For ex: template<typename Tvoid func(T); template<typename Tvoid func(T*); now, func<intis...
2
by: Emmanuel Deloget | last post by:
Hello, I'm trying to find a (sfinae powered) way to verify if a particular type declares a subtype (either using typedef or by declaring a subclass). To be more concrete, let's say that I'm...
5
by: Fei Liu | last post by:
Hello, I just hit a strange problem regarding SFINAE. The following code causes compile error (void cannot be array element type), I thought SFINA should match test(...) version instead and not...
2
by: Barry | last post by:
The problem brought by one earlier post from comp.lang.c++ http://groups.google.com/group/comp.lang.c++/browse_thread/thread/bf636c48b84957b/ I take part of the question and reproduce the code to...
35
by: James Kanze | last post by:
Just ran into an interesting question concerning SFINAE. Given the following code: #include <iostream> #include <typeinfo> template< typename T > class P { public:
0
by: greek_bill | last post by:
Hi, I have a template function for which I use SFINAE to restrict one of the parameters. Then I also have a partial specialization of this function.I would like to provide an explicit...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.