stgtscc
stgtscc

Reputation: 990

C++ template struct 'has no member named' error

The only way I can get this to compile without issuing a warning on no member a when T==B and so on for T==A is to reinterpret_cast within the if statement blocks and access the non-shared members via a pointer. Is there no way to get around this or hint to the compiler otherwise?

This is gcc 4.8.x

enum Type { A, B};

template<Type T> struct S { };
template<> struct S<A> { int x; int a; };
template<> struct S<B> { int y; int x; int b; };

template<Type T> static void foo(int x)
{
    // lots of code

    S<T> s;
    s.x = someCall();

    if (T == A)
    {
        s.a = 1;
    }
    else if (T == B)
    {
        s.y = 2;
        s.b = 3;
    }

    // a bunch of common code
}

Edit: I know about making a dedicated specific functions to handle the specifics but I was hoping to avoid extra boilerplate code.

Upvotes: 2

Views: 2432

Answers (2)

John Dibling
John Dibling

Reputation: 101456

if (T == A)
[...]
else if (T == B)
[...]

A, B and T here are compile-time entities, not run-time entities. And yet you are attempting to perform a run-time evaluation of something that is known at compile time. Now sure, you could try to hack up something with a kind of iffy cast (try, and fail), or try some other kind of kludgery, but the code is telling you: you're doing it wrong.

Specializing foo is one way, as may be levarging SFINAE (as with enable_if), but from what little I can see here of your use case, that would be doing it wrong too. That's because the code you look to be implementing is some kind of initialization code, or code that manipulates the internal state of the object being used. In a sense, you're violating the principle of single responsibility.

So move that code to where the responsibility belongs: in the class that's being inited.

template<> struct S<A> { int x; int a; void init() {a=1;} };
template<> struct S<B> { int y; int x; int b; void init() { x=2; y=3; } };

template<Type T> static void foo(int x)
{
    // lots of code

    S<T> s;
    s.x = someCall();

    s.init();

    // a bunch of common code
}

You could also use a factory class. This would abstract away both the instantiation of the object, and the call to init(), which can take different parameters depending on the ultimate type.

Upvotes: 1

Jarod42
Jarod42

Reputation: 217275

You may use specialization:

template <Type T> void bar(S<T>&);

template <> void bar(S<A>& s) {s.a = 1;}
template <> void bar(S<B>& s) {s.y = 2; s.b = 3;}

template<Type T> static void foo(int x)
{
    // lots of code

    S<T> s;
    s.x = someCall();

    bar(s);

    // a bunch of common code
}

Upvotes: 3

Related Questions