-3

Is using templates for specifying capacities of containers a good use or misue of the templates? Eg.

template <unsigned int Length>
class String {
public:
    String();
    String(String& other);
    String& operator=(String& rhs);
    ~String();

    void Append(String& other);

private:
    unsigned int length = Length;
};
4
  • 2
    It depends. Do you want to support e.g. String<10> ten; String<12> twelve; twelve.Append(ten);?
    – Caleth
    Mar 8, 2022 at 15:13
  • 2
    Could you please create an example where length is used in some sensible way? Nonsensical examples will attract downvotes and close votes.
    – Doc Brown
    Mar 8, 2022 at 15:35
  • 4
    Are you aware of std::array? - en.cppreference.com/w/cpp/container/array Mar 8, 2022 at 15:39
  • This question smells a bit of a principal mis-understanding of templates (or a very bad choice of example) - Why would a string of length 10 need to be a different type from a string of length 11?
    – tofro
    Mar 19, 2022 at 14:32

2 Answers 2

4

There is nothing technically wrong with this template. However, programs which use hard-coded upper limits for strings or containers tend to break the Zero-One-Infinity rule, which makes them prone to scale badly in the long run.

Nevertheless there are sometimes optimization requirements for which such constants can make sense. However, one should really think twice about the intended use cases before introducing such a construct into a program, which encourages to use compile-time constants instead of dynamic limits.

3

Templates for fixed sized containers is a valid practice. It fills some niche needs: the standard library offers for example std::array as a more consistent alternative to native fixed sized arrays. But it's not very flexible, as the more frequent use of std::vector over std::array may suggest.

The main issue is that each instantiation, e.g. String<10> or String<11>, is a different type, unrelated to its cousins. So your operator= could not assign a String<10> to a String<11>. And if you'd pass it as parameter to a function, the function itself should be a template function. So better keep this kind of templates for the cases where it's really critical to have fixed size containers.

There are other drawbacks that are less constraining. But you should be aware of :

  • A count of the number of active items may probably be needed (e.g. considering your Append()). A much more flexible variable-length container could then be obtained with a very little overhead.
  • If on contrary the container is systematically filled, all its items will get constructed when constructing the container: depending on the complexity of the default item, it might be a potential performance issue for containers of a very large size.
  • Compilers are not required to generate common code for String<10> and String<11>. You might therefore end with multiple copies of very similar binary code and a bloated executable.

Not related: your copy assignment overload should use the String& operator=(const String& rhs); signature: this would allow rhs to be a temporary or a constant String. More about recommended overloads here.

2
  • Maybe add: Proper type would be std::size_t. Also, std::span the other prominent example defines a compile-time fixed length view, and a dynamic view (the default), as well as explicit interactions. Mar 8, 2022 at 23:57
  • @Deduplicator Thanks for these interesting and valid remarks. Although I’d personally have used a size_t as you suggest, the fact is that I don’t know anything about OP’s container. If there is no promise about contiguity of storage, it could in theory very well be any other unsigned integral type (even unsigned long long if it is a sparse container or a file container) ;-)
    – Christophe
    Mar 10, 2022 at 9:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.