Compiler template bug in VC++ 7.1 with pointer to members

G

Gianni Mariani

I believe the code below is compliant with the C++ standard. GCC and
Comeau compiles this code without complaint however VC++ 7.1 has a
number of silly complaints.

Are there any known work arounds ?

template <typename T>
class foo
{
public:
typedef typename T::foo_inner foo_inner;

void func(
foo_inner * foo_inner::* member
)
{
}

void func2(
typename T::foo_inner * T::foo_inner::* member
)
{
}
};

VC++ 7.1 generates the following errors.

test.cpp(11) : error C2653: 'foo<T>::foo_inner' : is not a class or
namespace name

test.cpp(7) : see declaration of 'foo<T>::foo_inner'

test.cpp(20) : see reference to class template instantiation 'foo<T>'
being compiled

test.cpp(17) : error C2653: 'foo_inner' : is not a class or namespace name
 
D

David Lowndes

I believe the code below is compliant with the C++ standard. GCC and
Comeau compiles this code without complaint however VC++ 7.1 has a
number of silly complaints.

Gianni,

The latest Whidbey alpha compiler also exhibits the same errors, so
I'll try to pass them your report on to MS.
Are there any known work arounds ?

I'm afraid I don't know.

Dave
 
W

William M. Miller

Gianni Mariani said:
I believe the code below is compliant with the C++ standard. GCC and
Comeau compiles this code without complaint however VC++ 7.1 has a
number of silly complaints.

Are there any known work arounds ?

template <typename T>
class foo
{
public:
typedef typename T::foo_inner foo_inner;

void func(
foo_inner * foo_inner::* member
)
{
}

void func2(
typename T::foo_inner * T::foo_inner::* member
)
{
}
};

VC++ 7.1 generates the following errors.

test.cpp(11) : error C2653: 'foo<T>::foo_inner' : is not a class or
namespace name

test.cpp(7) : see declaration of 'foo<T>::foo_inner'

test.cpp(20) : see reference to class template instantiation 'foo<T>'
being compiled

test.cpp(17) : error C2653: 'foo_inner' : is not a class or namespace name

It appears that VC++ does a "trial instantiation" with an
invented type in order to parse the template, and they didn't
set up the nested "foo_inner" with the appropriate attributes
to be used as a nested-name-specifier in a qualified-id (or
they didn't suppress this particular error message -- I don't
know the internals of the compiler so I can't say for sure
which technique they used).

As for a workaround, you could do something like this:
template <typename T>
class foo
{
public:
typedef typename T::foo_inner foo_inner;
struct local_foo: T::foo_inner { };
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
void func(
foo_inner * local_foo::* member ^^^^^^^^^
)
{
}

void func2(
typename T::foo_inner * local_foo::* member ^^^^^^^^^
)
{
}
};

Ugly, but it should work okay.

-- William M. Miller
 
G

Gianni Mariani

William said:
As for a workaround, you could do something like this:



struct local_foo: T::foo_inner { };
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^



Ugly, but it should work okay.

I'm not sure.. I think there would be a member pointer conversion error.


struct A
{
int a;
};

struct B : A
{
int b;
};


typedef int A::* A_mptr;
typedef int B::* B_mptr;

A_mptr aptr1 = &B::a; // this seems ok (I'm not sure why)

B_mptr bptr1 = &B::a;

int main()
{
aptr1 = bptr1; // error ...
}


I did come up with a different work-around, I typedef'd the member to
pointer type in the class being passed into the template. So there was
no ptr to member parsing in the template.
 
W

William M. Miller

Gianni Mariani said:
I'm not sure.. I think there would be a member pointer conversion error.

No. See below.
struct A
{
int a;
};

struct B : A
{
int b;
};


typedef int A::* A_mptr;
typedef int B::* B_mptr;

A_mptr aptr1 = &B::a; // this seems ok (I'm not sure why)

The reason this is okay is that the type of "&B::a" is
"int A::*" -- it reflects the class in which the member
is declared, not the class used to name the member. If
you had said "&B::b", it would have been an error. See
below.
B_mptr bptr1 = &B::a;

int main()
{
aptr1 = bptr1; // error ...
}

Yes, that's an error, but that's not what I was doing above.
Remember, class type conversions and pointer-to-member
conversions are type-safe in opposite directions. You can
safely convert a pointer-to-derived into a pointer-to-base
because a derived object "is-a" base object -- or, equivalently,
because a derived object is guaranteed to have a base-class
subobject. You can safely convert a pointer-to-base-member
into a pointer-to-derived-member because it's guaranteed that
the derived class will have every member that the
pointer-to-base-member might be pointing to, while the inverse
is not necessarily true.

In my suggested workaround, calling foo<T>::func would involve
converting the argument from a T::foo_inner::* (i.e., a
pointer-to-base-member) to a foo<T>::local_foo::* (i.e., a
pointer-to-derived-member), which is the permitted direction.
Your example of "aptr1 = bptr1" attempts to convert in the
opposite direction, which is an error.
I did come up with a different work-around, I typedef'd the member to
pointer type in the class being passed into the template. So there was
no ptr to member parsing in the template.

That's fine, too, if you control the declarations of the classes
with which the template will be instantiated.

-- William M. Miller
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top