0

So, I'm new to using templates, and I have a question. Since templates are handled at compile time, and array size has to be set at compile time, can I use a template to set array size?

template<const size_t N> struct Stats {
    // Member functions (omitted).

    // The stats themselves.
    int stats[N];
};

class DudeWithStats {
    public:
        void setStats(int sts[], size_t sz, bool derived = false);

        // Other member functions (omitted).

    private:
        Stats<8> base;
        Stats<5> derived;

        // Other member variables (omitted).
};

void DudeWithStats::setStats(int sts[], size_t sz, bool drvd /* = false */) {
    for (int i = 0; i < sz; i++) {
        if (drvd) {
            derived.stats[i] = sts[i];
        } else {
            base.stats[i] = sts[i];
        }
    }
}

int main() {
    int arrBase[8] = { 10, 20, 10, 10, 30, 10, 15, 6 };
    int arrDerived[5] = { 34, 29, 42, 100, 3 };

    DudeWithStats example;
    example.setStats(arrBase, 8);
    example.setStats(arrDerived, 5, true);
}

I can see making a dynamic array with new or std::vector working out, but I'm curious as to whether this would work.

(Yes, I know const is meaningless in template declaration, at least to the compiler. It's mainly there for documentation.)

Thanks in advance.

(Edit: Noticed I had the default argument in setStats()' definition. Fixed that. Fixed the function itself, too, I believe (never had to directly copy an array before).)

(Edit: Switched it to a size_t. Still working on getting setStats() to work, I'll probably just stick with passing the stats manually instead of as an array.)

(Edit: Just used a workaround to get setStats() working. It looks a bit awkward, but it's good enough for test code.)


Thanks for answering and helping, everyone. I'll use something similar to that for what I need, and improve it as I get better at coding.

  • 2
    What happened when you tried it ? – Paul R Sep 28 '15 at 22:24
  • Your template argument should probably be of type `size_t` – clcto Sep 28 '15 at 22:26
  • If you do nothing else wrong, it will work. Only often it becomes quickly annoying that those arrays are all of different type. But if that is not an issue, this approach is okay. – BitTickler Sep 28 '15 at 22:26
  • 3
    This kind of thing won't work, regardless of templates: `derived.stats = sts;` Arrays are not assignable. – juanchopanza Sep 28 '15 at 22:27
  • There are a few other errors too, but the general idea is fine. – Paul R Sep 28 '15 at 22:28
  • Okay, thanks. @Paul R: Haven't tried it yet, it's an idea for a change I'm making. I wanted to ask before trying because I know some compilers are a bit loose with array bounds. Gonna do that now. – Justin Time - Reinstate Monica Sep 28 '15 at 22:51
  • @BitTickler: Yeah, just figured it'd be useful for keeping track of stats. That's a bit of a simplified example, the struct would actually contain three arrays of the same size (for raw stats, "functional" stats with equipment, and in-battle stats (including buffs/debuffs)), I wanted an easy way to access both base and derived stats that used the same syntax, but didn't look awkward. – Justin Time - Reinstate Monica Sep 28 '15 at 22:56
  • @juanchopanza: Yeah, that's my bad. xD – Justin Time - Reinstate Monica Sep 28 '15 at 22:57
  • 1
    If you use `std::array stats;` then you can use assignment operator to do copying of the array. – M.M Sep 28 '15 at 23:18

2 Answers2

3

Yes. Yes, you can.

template <size_t N>
void foo()
{
   // 100% standard-compliant and fine
   bool array[N] = {};
}
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
1

You're completely right about the templates stuff. Templates are (maybe too?) flexible, with a system that (IMHO) is only compared by Haskell's.

Anyway, GCC won't like your code:

c.cpp:21:57: error: default argument given for parameter 2 of ‘void DudeWithStats::setStats(int*, bool)’ [-fpermissive]
 void DudeWithStats::setStats(int* sts, bool drvd = false) {
                                                         ^
c.cpp:10:14: error: after previous specification in ‘void DudeWithStats::setStats(int*, bool)’ [-fpermissive]
     void setStats(int* stats, bool derived = false);
          ^

The default parameter is given once and only once, remove it from the definition and you'll be fine.

c.cpp: In member function ‘void DudeWithStats::setStats(int*, bool)’:
c.cpp:23:23: error: incompatible types in assignment of ‘int*’ to ‘int [5]’
         derived.stats = sts;
                       ^
c.cpp:25:20: error: incompatible types in assignment of ‘int*’ to ‘int [8]’
         base.stats = sts;
                    ^

Who did said you arrays are assignable? There's memcpy and new(&base.stats) for some reason, no?

c.cpp: In function ‘int main()’:
c.cpp:34:13: error: request for member ‘setStats’ in ‘example’, which is of non-class type ‘DudeWithStats()’
     example.setStats(arrBase);
             ^
c.cpp:35:13: error: request for member ‘setStats’ in ‘example’, which is of non-class type ‘DudeWithStats()’
     example.setStats(arrDerived, true);

........

Tutorials have lied you! You're not declaring a variable, but a function!

There's an ambiguity in C++ syntax (at least from the programmer's perspective) that makes things like DudeWithStats example(); to mean "declare a function (defined somewhere else) that takes no arguments, and returns a DudeWithStats" rather than "declare a DudeWithStats named example and default-initialize it".

For the later purposes, use DudeWithStats example; (with no parentheses!). And, for brave and standards-compliant souls, C++11 supports this non-ambiguous alternative syntax found in DudeWithStats example{};. This syntax does avoid the ambiguity presented above, and it provide several other advantages, including the resolution of the (even more) confuzzling most vexing parse problem.

So the answer is...

Apart from those minor details, your code and use of templates is (syntactically) correct. P.D: May you use size_t instead of unsigned const int for everyone's mental health, please?

Community
  • 1
  • 1
3442
  • 8,248
  • 2
  • 19
  • 41
  • This answer would be better without denigrating braced initialization... many of us like it – M.M Sep 28 '15 at 23:18
  • @M.M: I'm not trying to *denigrate* it, but rather state that I don't like the aesthetical way it was decided to be. It appears to me that they just took the last symbol pair that would resolve such ambiguity (`{` and `}`) and didn't considered something like `MyType myVariable -> (firstArg, secondArg)` or whatever. Anyway, they're still technically superior, and I'll edit to avoid further misunderstandings. – 3442 Sep 28 '15 at 23:53