Jean-Baptiste
Jean-Baptiste

Reputation: 967

Template parameter shadowing with GCC 5

I had the (bad) surprise to discover that the following code does not compile using CGG 5 or below, although it works like a charm with clang 4 or CGG 6 (and higher).

I don't really see what's going wrong, and what it shadowing template parameters of class B. More importantly I don't see how I should tweak it so that it compiles with older versions of GCC...

#include <array>

template <typename T, int N>
struct A {
public:
    std::array<T, 3> coordinates = { };
};

template <typename T, int N>
class B {
public:
    A<T, N> *myA = new A<T, N>();
};

Compiler outputs:

<source>:12:29: error: expected ';' at end of member declaration
     A<T, N> *myA = new A<T, N>();
                             ^
<source>:12:29: error: declaration of 'A<T, N> B<T, N>::N'
<source>:9:23: error:  shadows template parm 'int N'
 template <typename T, int N>
                       ^
<source>:12:30: error: expected unqualified-id before '>' token
     A<T, N> *myA = new A<T, N>();
                              ^
<source>:12:26: error: wrong number of template arguments (1, should be 2)
     A<T, N> *myA = new A<T, N>();
                          ^
<source>:4:8: error: provided for 'template<class T, int N> struct A'
 struct A {
        ^
Compiler exited with result code 1

Upvotes: 1

Views: 370

Answers (2)

Daniel H
Daniel H

Reputation: 7463

This is a GCC5 bug. You can work around it in various ways. The simplest is probably to add parentheses around the new expression, as pointed out in the comments:

template <typename T, int N>
class B {
public:
    A<T, N> *myA = (new A<T, N> ());
};

Another way, perhaps a good idea on its own if you use the type a lot, would be to add using a_type = A<T, N>; to the class, and then saying new a_type:

template <typename T, int N>
class B {
private:
    using a_type = A<T, N>;
public:
    A<T, N> *myA = new a_type();
};

Although it doesn’t seem to be necessary, I added a main function to ensure template instantiation just in case that affected the bug:

int main() {
    B<int, 5> b1, b2;
    b1.myA->coordinates = {{1, 2, 3}};
    return b2.myA->coordinates.size();
}

Additionally, I assume these are just artifacts of making a minimal example, but just in case, a few additional points:

  • Your class B has a memory leak: it never deletes the pointers it news.
  • Depending on coding style, it may be more idiomatic to initialize the variable in the constructor if it has a complex initialization, unless it’s supposed to be static.
  • Based on just what you’ve shown us, a pointer in class B is an unnecessary level of indirection and the A (or just the std::array) should be a direct member.

Upvotes: 2

You should initialize your pointer in a constructor, not in-class. Like so:

template<typename T, int N>
class B {
public:
    B() : myA(new A<T, N>()) {}
    ~B() { delete myA; }
    A<T, N> *myA;
};

Or, you could simply ditch the pointer:

template<typename T, int N>
class B {
public:
    A<T, N> myA;
};

If you plan on using the pointer version, keep in mind that there are better ways to do this (std::unique_ptr).

Upvotes: 1

Related Questions