struct syntax in .h

I'm looking through some somewhat complex code and trying to understand what's happening. I see some syntax inside of a struct that I don't understand.

In the code below (which I've copied verbatim), my question is about the first 3 lines of the struct definition. What is that?

Elsewhere in the code, the SmartScriptHolder is used as an object, or what I might think of as a multi-dimensional array in other programming languages, where each index contains an array of data pulled from a database. Please forgive my loose use of the terms array and index in this case. I know they're wrong, but I don't know the right words.

It looks like a function call with no parameters, but the colon after SmartScriptHolder() makes it look like it's calling a base class method.

If it was calling a base class method, wouldn't there be only one method?

SmartScriptHolder isn't inheriting from another class, so my base class method idea is bogus anyway.

I know I'm being stupid here somewhere in my understanding of this, but I can't figure out where.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// one line in DB is one event
struct SmartScriptHolder
{
    SmartScriptHolder() : entryOrGuid(0), source_type(SMART_SCRIPT_TYPE_CREATURE)
        , event_id(0), link(0), timer(0), active(false), runOnce(false)
        , enableTimed(false) {}

    int32 entryOrGuid;
    SmartScriptType source_type;
    uint32 event_id;
    uint32 link;

    SmartEvent event;
    SmartAction action;
    SmartTarget target;

    public:
        uint32 GetScriptType() const { return (uint32)source_type; }
        uint32 GetEventType() const { return (uint32)event.type; }
        uint32 GetActionType() const { return (uint32)action.type; }
        uint32 GetTargetType() const { return (uint32)target.type; }

    uint32 timer;
    bool active;
    bool runOnce;
    bool enableTimed;
};


If you're curious, this code is from SmartScriptMgr.h in TrinityCore.

Thank you.
The first three lines are the constructor. The colon after its name is part of the C++ constructor syntax, see for example http://en.cppreference.com/w/cpp/language/initializer_list
Okay, I guess I understand the member initializer list. Does this work differently from defining the values of the members in the constructor?

For example, would the following code yield the same result?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// one line in DB is one event
struct SmartScriptHolder
{
    /*
    SmartScriptHolder() : entryOrGuid(0), source_type(SMART_SCRIPT_TYPE_CREATURE)
        , event_id(0), link(0), timer(0), active(false), runOnce(false)
        , enableTimed(false) {}
    */

    SmartScriptHolder() {
        entryOrGuid = 0;
        source_type = SMART_SCRIPT_TYPE_CREATURE;
        event_id = 0;
        link = 0;
        timer = 0;
        active = false;
        runOnce = false;
        enableTimed = false;
    }

    int32 entryOrGuid;
    SmartScriptType source_type;
    uint32 event_id;
    uint32 link;

    SmartEvent event;
    SmartAction action;
    SmartTarget target;

    public:
        uint32 GetScriptType() const { return (uint32)source_type; }
        uint32 GetEventType() const { return (uint32)event.type; }
        uint32 GetActionType() const { return (uint32)action.type; }
        uint32 GetTargetType() const { return (uint32)target.type; }

    uint32 timer;
    bool active;
    bool runOnce;
    bool enableTimed;
};


Thank you.
the difference is the same as between
1
2
 
uint32 entryOrGuid = 0;

and
1
2
3
 
int32 entryOrGuid;
entryOrGuid = 0;

one is initialization, the other is assignment. The end result is the same in this case.
> Does this work differently from defining the values of the members in the constructor?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <cmath>

struct A
{
    // *** warning *** the value of A::second is used before it is initialized
    // initialization of non-static members are in order of declaration
    // A::first will be initialized before A::second
    // behaviour of this constructor changes if we change the order of declaration
    A( int a ) : second( 4.3*a*a - 2.6*a + 3.5 ), first( std::sqrt(second) ) {}

    A( long a )
    {
        // assignments take place in the order that we specify
        // A::second is assigned to, and then, A::first will be assigned to
        // behaviour of this constructor does not change if we change the order of declaration
        second = 4.3*a*a - 2.6*a + 3.5 ;
        first = std::sqrt(second) ;
    }

    double first ;
    double second ;
};

int main()
{
    A a1( 23 ) ;
    std::cout << a1.first << ' ' << std::sqrt(a1.second) << '\n' ;

    A a2( 23L ) ;
    std::cout << a2.first << ' ' << std::sqrt(a2.second) << '\n' ;
}

http://coliru.stacked-crooked.com/a/e4e4259897f7347a
So, in your constructor that uses an int as a parameter, the initialization of A::first happens first because it's declared first, but that initialization fails because it's based on the value of the other variable (A::second) which hasn't been initialized yet.

When using a member initializer list, the definition of the variables happens in the order of their declaration regardless of their order in the list.

When I define the values of the variables in the constructor, the definition happens, as expected, in the order that I define them regardless of their declaration order.

Did I get that right?

I created a simpler example for myself ... http://coliru.stacked-crooked.com/a/8e0859b6b83f0b2f

Okay, it's not really simpler, but the variable names are easier to talk about :)

I don't think it really matters in the code I'm currently looking at, but I can see how this could cause a problem if I didn't know about it.

It's also interesting that it only generated a warning and the result was 0 rather than an error from trying to do math with an undefined variable.

Thank you.
> When using a member initializer list, the definition of the variables happens
> in the order of their declaration regardless of their order in the list.

> When I define the values of the variables in the constructor, the definition happens,
> as expected, in the order that I define them regardless of their declaration order.

> Did I get that right?

Yes.

When using a member initializer list, the definition initialization of the variables happens in the order of their declaration regardless of their order in the list.

When I define assign to the values of the variables in the constructor, the definition assignment happens, as expected, in the order that I define assign to them regardless of their declaration order.

1
2
3
4
5
struct A 
{
       const int n ; // A::n must be initialized, it can't be assigned to
       A( int v ) : n(v) {} 
};



> It's also interesting that it only generated a warning and the result was 0
> rather than an error from trying to do math with an undefined variable.

A construct that may engender undefined behaviour need not be diagnosed.

Note: It is not possible for a compiler to determine that a particular construct will definitely cause undefined behaviour. With:

1
2
3
4
5
struct struct MemberInitializer {
    MemberInitializer(int nbr): d2(nbr*2), d1(d2*3) {}
    int d1;
    int d2;
};


It is possible that the type MemberInitializer is used only in name. Or that every object of type MemberInitializer that is instantiated has a static storage duration (when members d1 and d2 would be default initialized). It is also possible that the contents of the memory occupied by an uninitialized int happens to have a valid object representation of an int.
Last edited on
Topic archived. No new replies allowed.