Special case of templates, pointers-to-members, inheritance - works in VC++ 2008, GCC 3.4.4, but fails in VC++ 2005
-
Hi All, I'm hitting my head against the wall third day about the following code:
//#define PointerToMember(B) B::*
#define PointerToMember(B)template <bool A, typename B, typename C, C PointerToMember(B) D>
class One
{
public:
One() {};
};template <typename B, typename C, C PointerToMember(B) D>
class One<true, B, C, D> : public One<false, B, C, D>
{
public:
One<true, B, C, D>() : One<false, B, C, D>() { };
};template <bool A, typename B>
struct Two
{
template <typename C, C PointerToMember(B) D>
class OneUsage : public One<A, B, C, D> { };
};template <typename B>
struct Two<true, B> : public Two<false, B>
{
template <typename C, C PointerToMember(B) D>
class OneUsage : public One<true, B, C, D> { };};
struct S { int i;};
int main()
{
//Two<true, S>::OneUsage<int, &S::i> two;
Two<true, S>::OneUsage<int, 1> two;
return 0;
}With those two lines commented out, it compiles in all 3 compilers: GCC, VC++ 2008 and VC++ 2005. But, if instead of
//#define PointerToMember(B) B::*
#define PointerToMember(B)
...
//Two<true, S>::OneUsage<int, &S::i> two;
Two<true, S>::OneUsage<int, 1> two;you will make it this:
#define PointerToMember(B) B::*
//#define PointerToMember(B)
...
Two<true, S>::OneUsage<int, &S::i> two;
//Two<true, S>::OneUsage<int, 1> two;it still will compile in GCC and 2008, but will fail in 2005 with error: error C2955: 'Two<A,B>::OneUsage' : use of class template requires template argument list with [ A=true, B=S ] list4.cpp(30) : see declaration of 'Two<A,B>::OneUsage' with [ A=true, B=S ] error C2133: 'two' : unknown size error C2512: 'Two<A,B>::OneUsage' : no appropriate default constructor available with [ A=true, B=S ] Looks like VC++ 2005 compiler does not like usage of pointer-to-member type declaration when inheriting templates with partial specialization. Is it a bug of VC++ 2005, or I break some C++ standard, and other two compilers are just more forgiving? This code was taken from much more complex one just to show exact problem. Code compiled by both VC++ 2008 and GCC works with no issue and as
-
Hi All, I'm hitting my head against the wall third day about the following code:
//#define PointerToMember(B) B::*
#define PointerToMember(B)template <bool A, typename B, typename C, C PointerToMember(B) D>
class One
{
public:
One() {};
};template <typename B, typename C, C PointerToMember(B) D>
class One<true, B, C, D> : public One<false, B, C, D>
{
public:
One<true, B, C, D>() : One<false, B, C, D>() { };
};template <bool A, typename B>
struct Two
{
template <typename C, C PointerToMember(B) D>
class OneUsage : public One<A, B, C, D> { };
};template <typename B>
struct Two<true, B> : public Two<false, B>
{
template <typename C, C PointerToMember(B) D>
class OneUsage : public One<true, B, C, D> { };};
struct S { int i;};
int main()
{
//Two<true, S>::OneUsage<int, &S::i> two;
Two<true, S>::OneUsage<int, 1> two;
return 0;
}With those two lines commented out, it compiles in all 3 compilers: GCC, VC++ 2008 and VC++ 2005. But, if instead of
//#define PointerToMember(B) B::*
#define PointerToMember(B)
...
//Two<true, S>::OneUsage<int, &S::i> two;
Two<true, S>::OneUsage<int, 1> two;you will make it this:
#define PointerToMember(B) B::*
//#define PointerToMember(B)
...
Two<true, S>::OneUsage<int, &S::i> two;
//Two<true, S>::OneUsage<int, 1> two;it still will compile in GCC and 2008, but will fail in 2005 with error: error C2955: 'Two<A,B>::OneUsage' : use of class template requires template argument list with [ A=true, B=S ] list4.cpp(30) : see declaration of 'Two<A,B>::OneUsage' with [ A=true, B=S ] error C2133: 'two' : unknown size error C2512: 'Two<A,B>::OneUsage' : no appropriate default constructor available with [ A=true, B=S ] Looks like VC++ 2005 compiler does not like usage of pointer-to-member type declaration when inheriting templates with partial specialization. Is it a bug of VC++ 2005, or I break some C++ standard, and other two compilers are just more forgiving? This code was taken from much more complex one just to show exact problem. Code compiled by both VC++ 2008 and GCC works with no issue and as
OK, question still remains, but I solved the problem by moving declaration of pointer-to-member this way:
//#define PointerToMember(B) B::*
#define PointerToMember(B)Two<true, S>::OneUsage<int S::*, &S::i> two;
//Two<true, S>::OneUsage<int, 1> two;Pay attention to this line:
Two<true, S>::OneUsage<int S::*, &S::i> two;
Basically, instead of saying to template in the declaration that passed variable is of type pointer-to-member, I just make typename "C" to be pointer-to-int-member. Problem with this is that now I don't know the final type of variable "D", and if I will ever need it, I would need to pass it separately, i.e. adding one more template parameter. This variant compiles everywhere, but question still remains: what's wrong with previous one? It's more compact and straightforward...
-
Hi All, I'm hitting my head against the wall third day about the following code:
//#define PointerToMember(B) B::*
#define PointerToMember(B)template <bool A, typename B, typename C, C PointerToMember(B) D>
class One
{
public:
One() {};
};template <typename B, typename C, C PointerToMember(B) D>
class One<true, B, C, D> : public One<false, B, C, D>
{
public:
One<true, B, C, D>() : One<false, B, C, D>() { };
};template <bool A, typename B>
struct Two
{
template <typename C, C PointerToMember(B) D>
class OneUsage : public One<A, B, C, D> { };
};template <typename B>
struct Two<true, B> : public Two<false, B>
{
template <typename C, C PointerToMember(B) D>
class OneUsage : public One<true, B, C, D> { };};
struct S { int i;};
int main()
{
//Two<true, S>::OneUsage<int, &S::i> two;
Two<true, S>::OneUsage<int, 1> two;
return 0;
}With those two lines commented out, it compiles in all 3 compilers: GCC, VC++ 2008 and VC++ 2005. But, if instead of
//#define PointerToMember(B) B::*
#define PointerToMember(B)
...
//Two<true, S>::OneUsage<int, &S::i> two;
Two<true, S>::OneUsage<int, 1> two;you will make it this:
#define PointerToMember(B) B::*
//#define PointerToMember(B)
...
Two<true, S>::OneUsage<int, &S::i> two;
//Two<true, S>::OneUsage<int, 1> two;it still will compile in GCC and 2008, but will fail in 2005 with error: error C2955: 'Two<A,B>::OneUsage' : use of class template requires template argument list with [ A=true, B=S ] list4.cpp(30) : see declaration of 'Two<A,B>::OneUsage' with [ A=true, B=S ] error C2133: 'two' : unknown size error C2512: 'Two<A,B>::OneUsage' : no appropriate default constructor available with [ A=true, B=S ] Looks like VC++ 2005 compiler does not like usage of pointer-to-member type declaration when inheriting templates with partial specialization. Is it a bug of VC++ 2005, or I break some C++ standard, and other two compilers are just more forgiving? This code was taken from much more complex one just to show exact problem. Code compiled by both VC++ 2008 and GCC works with no issue and as
I can't comment on what's wrong with your code in the context of VC++2005 - I don't tend to use pointers to members much and never as template arguments. However as your code is compiling and working on two other compilers I'd put it down to VC++2005 not being standard compliant in that area. I'd be tempted to try VC++2010, gcc 4.x and an EDG based as well and see what happens. If those three work then you can be fairly sure your code is standard. One other point - templates with templated internal types are going to confuse most people. Make sure you keep a clean interface between your code and your users or they're going to end up cursing your name. Something like bind_first or make_pair to hide all the gribbly declarations they might otherwise have to make (and which are a godsend with C++0x and the auto keyword). Cheers, Ash
modified on Thursday, July 8, 2010 4:03 AM