I looked up the following code based on a previous post. The example
determines whether a class has a member function named 'func'. It
works but I don't fully understand why:
template <typename Type>
class has_member
{
class yes { char m;};
class no { yes m[2];};
struct BaseMixin
{
void func(){}
};
struct Base : public Type, public BaseMixin {};
template <typename T, T t> class Helper{};
template <typename U>
static no deduce(U*, Helper<void (BaseMixin::*)(), &U::func>* =
0);
static yes deduce(...);
public:
static const bool result = sizeof(yes) == sizeof(deduce((Base*)
(0)));
};
struct With
{
void func(int, long) const{}
};
struct WithTemplate
{
template <typename T, typename U>
void func(T, U){}
};
struct Without
{
};
int main()
{
int check1[has_member<With>::result? 1 : -1];
int check2[has_member<WithTemplate>::result? 1 : -1];
int check3[!has_member<Without>::result? 1 : -1];
return 0;
}
I can see that the compiler will not be able to use the first 'deduce'
if Type has a member named 'func', because &U::func will be
ambiguous. It's what happens when Type <i>doesn't<\i> have a 'func'
member that is confusing. In that case it looks like the template
args are void member function from BaseMixin, and reference to 'func'
from Base, which may or may not be a void function. And the class no
is created with two ctor args?
Obviously there's a lot of C++ I don't know! Hope someone can explain
it. Thanks!
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
dec4106
|
11/16/2010 8:46:40 PM |
|
On 17 nov, 03:46, dec4106 <dec4...@gmail.com> wrote:
> I looked up the following code based on a previous post. The example
> determines whether a class has a member function named 'func'. It
> works but I don't fully understand why:
&U::func is being evaluated in an SFINAE context to define an overload
to 'deduce'.
If U::func is not valid, instead of generating an error, the overload
will be masked, and the other 'deduce' overload will be selected.
The second 'deduce' overload uses '...' to avoid ambiguousness in the
case where both overloads are available, as '...' is always the worst
match possible in overload resolution.
Both 'deduce' overloads return two different-sized types which are
used to tell which overload was invoked at compile-time.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Mathias
|
11/17/2010 3:01:24 PM
|
|
On Nov 17, 3:46 am, dec4106 <dec4...@gmail.com> wrote:
> I looked up the following code based on a previous post. The example
> determines whether a class has a member function named 'func'. It
> works but I don't fully understand why:
>
> template <typename Type>
> class has_member
> {
> class yes { char m;};
> class no { yes m[2];};
>
> struct BaseMixin
> {
> void func(){}
> };
>
> struct Base : public Type, public BaseMixin {};
>
> template <typename T, T t> class Helper{};
>
> template <typename U>
> static no deduce(U*, Helper<void (BaseMixin::*)(), &U::func>* =
> 0);
> static yes deduce(...);
>
> public:
> static const bool result = sizeof(yes) == sizeof(deduce((Base*)
> (0)));
>
> };
>
> struct With
> {
> void func(int, long) const{}
>
> };
>
> struct WithTemplate
> {
> template <typename T, typename U>
> void func(T, U){}
>
> };
>
> struct Without
> {
>
> };
>
> int main()
> {
> int check1[has_member<With>::result? 1 : -1];
> int check2[has_member<WithTemplate>::result? 1 : -1];
> int check3[!has_member<Without>::result? 1 : -1];
>
> return 0;
>
> }
>
> I can see that the compiler will not be able to use the first 'deduce'
> if Type has a member named 'func', because &U::func will be
> ambiguous. It's what happens when Type <i>doesn't<\i> have a 'func'
> member that is confusing. In that case it looks like the template
> args are void member function from BaseMixin, and reference to 'func'
> from Base, which may or may not be a void function. And the class no
> is created with two ctor args?
You are close, but not yet there.
Lets start from the beginning, the sizeof expression:
'sizeof(deduce((Base*)0))' must be read as 'sizeof(<function call>)'.
This in turn is the same as 'sizeof(<return value of function>)'.
To determine the size of the return value, we must determine which
function would be called if the sizeof-context were missing. This is
where overload resolution kicks in.
We know the function is called 'deduce' and there are two
possibilities:
- A non-template function
yes deduce(...)
- A function template:
template <typename U>
no deduce(U*, Helper<void (BaseMixin::*)(), &U::func>* = 0)
Based on the argument type in the call, the template parameter U is
deduced to be 'Base', and if class Type does not have a member called
'func', then '&U::func' uniquely resolves to '&BaseMixin::func' and
the template 'Helper' can be instantiated.
This gives s the overload set:
1. yes deduce(...)
2. no deduce(Base*, Helper<void (BaseMixin::*)(), &Base::func> = 0)
for the function call 'deduce((Base*)0)'
The second function is a better match (both can be called with a
single parameter; the second function requires no conversions, the
first function requires a conversion to ellipsis), so that function is
selected by the overload resolution.
Now that the function has been determined, we can go back to the
sizeof operator.
As deduce((Base*)0) resolved to the function returning a 'no' object,
the sizeof expression is equivalent to 'sizeof(no)'.
As you yourself already said, if Type contains a member named 'func',
then the ambiguity in '&U::func' causes the template to be removed
from the overload set, so only the function
yes deduce(...)
remains as an overload candidate in that case.
Bart v Ingen Schenau
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Bart
|
11/17/2010 3:02:35 PM
|
|
On 11/16/2010 9:46 PM, dec4106 wrote:
>
> I looked up the following code based on a previous post. The example
> determines whether a class has a member function named 'func'. It
> works but I don't fully understand why:
>
> template<typename Type>
> class has_member
> {
> class yes { char m;};
> class no { yes m[2];};
>
> struct BaseMixin
> {
> void func(){}
> };
>
> struct Base : public Type, public BaseMixin {};
>
> template<typename T, T t> class Helper{};
>
> template<typename U>
> static no deduce(U*, Helper<void (BaseMixin::*)(),&U::func>* =
> 0);
> static yes deduce(...);
>
> public:
> static const bool result = sizeof(yes) == sizeof(deduce((Base*)
> (0)));
> };
>
> struct With
> {
> void func(int, long) const{}
> };
>
> struct WithTemplate
> {
> template<typename T, typename U>
> void func(T, U){}
> };
>
> struct Without
> {
> };
>
>
> int main()
> {
> int check1[has_member<With>::result? 1 : -1];
> int check2[has_member<WithTemplate>::result? 1 : -1];
> int check3[!has_member<Without>::result? 1 : -1];
>
> return 0;
> }
>
> I can see that the compiler will not be able to use the first 'deduce'
> if Type has a member named 'func', because&U::func will be
> ambiguous. It's what happens when Type<i>doesn't<\i> have a 'func'
> member that is confusing. In that case it looks like the template
> args are void member function from BaseMixin, and reference to 'func'
> from Base, which may or may not be a void function. And the class no
> is created with two ctor args?
I'm not sure I understand your concern.
If the "Type" doesn't have a member "func", then class Base will inherit
just one member called "func". This "func" member will be the one
inherited from BaseMixin and we know that its type will be "void
(BaseMixin::*)()".
The idea is that if Type HAS a member called "func", then class Base
will inherit TWO members called "func" (one from Type and one from
BaseMixin) and applying an address-of opperator (&Base::func) will be
ambiguous. In turn, the ambiguity will force SFINAE to eliminate
function "no deduce()" from consideration of overloads and "yes
deduce(...)" will be chosen.
I'm not sure what you mean by "class no is created with two ctor args".
Class "no" is actually never created. It's just used to disambiguate
between two "deduce" functions. The argument to sizeof() isn't evaluated.
HTH,
Andy.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Andy
|
11/17/2010 8:01:48 PM
|
|
|
3 Replies
96 Views
(page loaded in 0.075 seconds)
|