Why is this still not allowed by gcc while MSVC does(NULL as template-argmument)?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I posted this message several days ago, improperly on gcc@xxxxxxxxxxx . But I think my question is not entirely solved, so I post it here again.

My original message:

    Please check the following small program:

    #include <stdio.h>

template<typename USER_TYPE, typename RET_TYPE, RET_TYPE (*pfn)(USER_TYPE), USER_TYPE valInvalid> class CEnsureCleanup_array
    {
        int m_ArraySize;
        USER_TYPE *m_ar;           // This member representing the object array ptr

    public:
        // Default constructor clears all pointer elements to invalid-value
        CEnsureCleanup_array(int ArraySize) : m_ArraySize(ArraySize) {
            m_ar = new USER_TYPE[m_ArraySize];
for(int i=0; i<m_ArraySize; i++) m_ar[i] = valInvalid; } // The destructor performs the cleanup.
        ~CEnsureCleanup_array() { Cleanup(); }
// Helper methods to tell if the value represents a valid object or not..
        int IsValid(int idx) { return(m_ar[idx] != NULL); }
        int IsInvalid(int idx) { return(!IsValid(idx)); }

        USER_TYPE& operator[](int idx) {
            return m_ar[idx];
        }

        operator USER_TYPE*() {
            return m_ar; // Return the address of the first array element.
        }

void Cleanup() { for(int i=0; i<m_ArraySize; i++)
                if (IsValid(i)) {
                    pfn(m_ar[i]);         // Cleanup the object.
                }
            delete m_ar;
        }
    };

    struct MyClass
    {
        int m_i;

        MyClass(int i) : m_i(i) {}
        ~MyClass() {
            printf("MyClass dtor(%d)\n", m_i);
        }
    };

    #define MakeCleanupClass_array(CecClassName, RET_TYPE_of_CleanupFunction, pCleanupFunction, USER_TYPE, valInvalid) \
        typedef CEnsureCleanup_array<USER_TYPE, RET_TYPE_of_CleanupFunction, pCleanupFunction, valInvalid> CecClassName;

    void DeleteMyClass(MyClass *p){ delete p; }

    MakeCleanupClass_array(CecMyClass, void, DeleteMyClass, MyClass*, 0)

    int main()
    {
        CecMyClass armc(2);
            // By using CecMyClass, armc[0] & armc[1] can be automatically deleted
            // on `armc' destruction.
        armc[0] = new MyClass(0);
        armc[1] = new MyClass(1);
return 0;
    }

    It compiles OK on Microsoft Visual Studio.NET 2005 or even MSVC
    6.0 SP6,and the output is:

    MyClass dtor(0)
    MyClass dtor(1)
    However, with gcc 4.1.0(SUSE Linux), compilation error:

    t1.cpp:56: error: could not convert template argument '0' to 'MyClass*'
    t1.cpp:56: error: invalid type in declaration before ';' token
    t1.cpp: In function 'int main()':
    t1.cpp:63: error: invalid types 'CecMyClass[int]' for array subscript
    t1.cpp:64: error: invalid types 'CecMyClass[int]' for array subscript

    I'm really baffled! Since 0 can be assigned to any pointer
    variable, then why "0 could not be converted to MyClass* "?

    Furthermore, if a try to change the line just above the main()
    definition to:

    MakeCleanupClass_array(CecMyClass, void, DeleteMyClass, MyClass*, (MyClass*)0)

    Compiler error changes to:

    t1.cpp:56: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
    t1.cpp:56: error: template argument 4 is invalid
    t1.cpp:56: error: invalid type in declaration before ';' token
    t1.cpp: In function 'int main()':
    t1.cpp:63: error: invalid types 'CecMyClass[int]' for array subscript
    t1.cpp:64: error: invalid types 'CecMyClass[int]' for array subscript
    I really cannot figure out why gcc spout those errors while that
    case of template usage has very clear meaning and is absolutely
    unambiguous.

    Can you dear gcc developers give me an explanation?

And later, Ian Lance Taylor replied:

The C++ standard is clear: "Although 0 is a valid template-argument
for a non-type template-parameter of integral type, it is not a valid
template-argument for a non-type template-parameter of pointer type."
(14.3.2 Template non-type arguments [temp.arg.nontype]).

Oh yes, according to Ian, I found that very statement on page 243 of ISO/IEC 14882. But why that rule? If you try that code with MSVC, you'll know that that code works just what I(and everyone, I think) expect, no language and semantic ambiguity!

Then, only after I change the problematic line to (two lines this time):

MyClass g_McNull = 0;
MakeCleanupClass_array(CecMyClass, void, DeleteMyClass, MyClass*, &g_McNull)

can gcc compiles in peace. Again, I want to ask: Since an global object address(a constant after link) can be accepted as the template argument, *then why NULL is forbidden?!*

A standard is not a bible, a standard is created by human and work for human. It may contain flaws, vagueness, or even errors. What I want to ask gcc gurus here regarding this problem is: If we allow NULL to be a valid "template-argument for a non-type /template-parameter/ of pointer type", does it cause harm to C++ code already existed in the world? Since C++ 98 allows an int number for a "non-type /template-parameter/ of *integral type*", then why in the world does he not allow 0 for a "non-type /template-parameter/ of pointer type"? It seems the standard does not state this. If you know, I'm very grateful to hear from you.



[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux