0

I have a class myclass that depends on some int for, say, setting up the size of a vector member. I can implement that as a non-type template parameter

template<int sz>
class myclass {
...

or

class myclass {
...

and then simply use sz as a parameter in the constructor or other class methods.

Both would work in many cases. In some other cases (e.g., if myclass refers to other templated classes or functions using sz as a non-type template parameter), only the first option would work. In cases where both can work, what are the possible reasons to prefer one or the other?

Besides my new coding in the future, this would also impact in what I do with some code I already have... whether to make efforts in "converting" one type of implementation to the other, or leave it as it is now.

I am not only asking about the differences (e.g., allocation at compile vs. runtime), but also how these differences may make one or the other option preferable.

  • 1
    Does anything except the constructor need to know the value? Are you ok with the value affecting the type? Are you ok with the value having to be a compile-time constant. – HolyBlackCat May 16 '21 at 13:55
  • 3
    Just an element: `myclass<1>` and `myclass<2>` are two different types; two object of a non-template `myclass` (the first one initialized with `1`, the second one with `2`) are objects of the same type. There isn't a best or worst: different needs bring to different solutions. – max66 May 16 '21 at 14:08
  • 3
    Not the downvoter, but the words "prefer" and "preferable" have little meaning without a description of your particular use case. – MatG May 16 '21 at 14:32
  • I researched it, and almost gave an my "opinion" as an answer, but after careful thought there is no right or wrong. If you read this question, https://stackoverflow.com/questions/5979723/do-templates-actually-have-to-be-compile-time-constructs, and its comments, you get to the conclusion that you might be invoking UB. (there is one point about someone from Intel saying that some cases should not even compile) –  May 16 '21 at 14:43
  • @HolyBlackCat - 1) As of now, only the constructor. But I foresee that I would need it in other methods in the future. 2) I don't understand this question. 3) Are you distinguishing *constant* vs. *constant expression*? If not, I don't understand the question... I should be ok with the value having to be a compile-time constant, otherwise the templated version would not work at all. – sancho.s ReinstateMonicaCellio May 30 '21 at 23:40
  • @max66 - Good point. So in cases I need all such objects to be of the same class, I should go for the second option. Otherwise, this would probably not be a factor in the decision. – sancho.s ReinstateMonicaCellio May 30 '21 at 23:44
  • @MatG - My use case is embedded in a large code. I guess here there is no MCVE smaller than my complete code (especially since I don't know the answer to the question!). If there are pros and cons of one or the other alternatives (max66 pointed out one such factor), I would check whether they apply to my case. Even without specifying the use case, I guess it is quite feasible to cite possible pros and cons. I myself answered in cases/situations similar to this one. – sancho.s ReinstateMonicaCellio May 30 '21 at 23:47
  • @KPCT - The question you linked is interesting. I still have to read all the answers and comments, and make the connection with the present OP. – sancho.s ReinstateMonicaCellio May 30 '21 at 23:56

1 Answers1

1

First, the overlap (“where both can work”) is rather smaller than you might think. You can’t make a container of various specializations of a class template (absent type-erasure tricks). Any function that uses myclass for a parameter or return type must, if myclass is a template, either name a specialization or be templated itself. Even purely local usage overlaps only if the size is a constant expression.

If none of these issues have forced your hand, there are performance implications as well. Using several instantiations of a class template may increase the binary size, although aggressive inlining may reduce that cost. Using a runtime parameter may introduce additional runtime overhead, although aggressive inlining may reduce that too. You also can’t avoid heap allocation with a single type (unless you impose a maximum capacity—that you always pay for).

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • 1) "The overlap is smaller than you might think"... maybe, I didn't think exhaustively about the options. 2) Interesting linked question. Still have to understand the connction with this OP. – sancho.s ReinstateMonicaCellio May 31 '21 at 00:18
  • 3) Your implication (`myclass` is a template) + (`f` uses `myclass`) => (`f` is a template or specialization) is logically the same as I stated (other `X` templated classes or functions using `sz` as a non-type template parameter) + (`myclass` refers to `X`) => (`myclass` is a template), but with proposition (`myclass` is a template) as a [premise instead of the conclusion](https://en.wikipedia.org/wiki/Propositional_calculus). So I guess it has no incidence as a consideration in answering this OP. 4) I would have to test the incidence of the factors you mention. Thanks! – sancho.s ReinstateMonicaCellio May 31 '21 at 00:18
  • @sancho.sReinstateMonicaCellio: The “my clients have to be templated” is certainly relevant, since if you need, say, to be called by a virtual function or by main, it influences whether you want your “type” to be templated. Not all design is bottom-up. – Davis Herring May 31 '21 at 01:40