1

Is there a way (in C++17) to achieve something similar to a forward declare, in a template? What I want to achieve is something like this:

template<typename T, SizeType D, typename SizeType = int>

Obviously here D depends on SizeType, so it must come before it. But in that case I cannot set a default parameter unless D also has a default parameter (which I do not want). Basically I want to be able to "declare" SizeType before D, but "define" it after it.

Edit: Here is an example of how I would like to use it:

template<typename T, SizeType D, typename SizeType = int>
class StaticArray{};
//...
StaticArray<float, 5> s; // = StaticArray<float, 5, int>
StaticArray<float, (1<<40), size_t>; // 1<<40 doesn't fit in int
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
lightxbulb
  • 1,251
  • 12
  • 29
  • Perhaps something like `extern template` could work? But then you need to already know about all instantiations that you're going to use... see also [here](https://stackoverflow.com/questions/8130602/using-extern-template-c11) – andreee Jul 04 '19 at 08:56
  • 1
    Do you want to allow e.g. `foo` and have it do the conversion? How about just `foo` and always deduce `SizeType`? – Quentin Jul 04 '19 at 08:56
  • @Quentin I don't care about conversion, I just want to have `SizeType` have a default parameter, while at the same time `D` should know about `SizeType` while coming before it in the parameter list. The issue here is that `D` doesn't know about `SizeType`. And if I swap the order, the issue is that I cannot give a default parameter to `SizeType` without setting one for `D`. – lightxbulb Jul 04 '19 at 09:00
  • 1
    Can't catch the problem. If you alway provide the parameter D it always will be deduced to the given type. So a "default" will never take place... I am wrong? – Klaus Jul 04 '19 at 09:05
  • 1
    @lightxbulb yes, I get what the problem *with your solution attempt* is, but your edit is what I was asking for. Although this shows an issue : the second case won't work, as `(1 << 40)` is done using only `int`s (and wouldn't compile) -- you'd need ``, which is redundant. Hence my (and Klaus') suggestion to just deduce `SizeType` since you kinda need to have it already. – Quentin Jul 04 '19 at 09:11
  • @Klaus I didn't know I could use auto in a parameter list - that was the issue. – lightxbulb Jul 04 '19 at 09:20
  • 1
    Note that with `template`, `StaticArray` and `StaticArray` are two different and unrelated types. A function like `f(StaticArray&)` won't accept `StaticArray`. – Evg Jul 04 '19 at 09:23
  • @Quentin I understood what your question was about after I figured it out - I didn't get it because I didn't know that one could use auto like that in the first place. So no, I did not want to force a conversion in this case - I wanted to have the type be defined by what's given as a non-type argument. So `5u` would for example induce an unsigned int type (I realize that may be an issue if one wants a specific size, like `uint32_t`). I get that `StaticArray` would be different from `StaticArray`, thank you for the clarification. – lightxbulb Jul 04 '19 at 09:23
  • 1
    @lightxbulb great! That was a bit of a chicken-and-egg situation :) – Quentin Jul 04 '19 at 09:26
  • @lightxbulb: Answers should go in the answer section. It's OK to answer your own question. – Nicol Bolas Jul 04 '19 at 13:30
  • @NicolBolas Is there any way I can see the history of edits to my post so that I can recover what you removed and put it in an answer below? – lightxbulb Jul 04 '19 at 14:16
  • 1
    @lightxbulb: Yes: press the "edited X time ago" link, and you can see the revision history of the question. – Nicol Bolas Jul 04 '19 at 14:19

2 Answers2

2

You could do something like this

template<typename T, auto DArg, typename SizeType = int, SizeType D = DArg>

Now the argument for D is provided first, then the type of D, and finally the argument is converted to D of the correct type.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Seems to work great even if it adds an extra non-type parameter (I assume one can then write something like: `StaticArray` which will be undesirable, but this is just a detail). If there's no better answer, I will accept yours after a while. – lightxbulb Jul 04 '19 at 09:08
  • @lightxbulb - If it was a function, you could prevent client code from specifying `D`. But for class templates, it's not possible to my knowledge. – StoryTeller - Unslander Monica Jul 04 '19 at 09:10
  • Thanks to your example I came up with a solution that best fits my problem, while not introducing a redundant typename, see the edits. Thank you very much for the help - I wouldn't have come up with that without your solution (and I think that your solution actually does solve the problem that I formulated here better). – lightxbulb Jul 04 '19 at 09:19
0

I came up with something else considering StoryTeller's answer and the comments:

template<typename T, auto D>

Minimal solution example for future readers:

#include <iostream>
template<typename T, auto D>
class StaticArray
{
public:
    using size_type = decltype(D);
};

int main()
{
    StaticArray<float, 5ul> s;
    std::cout << typeid(decltype(s)::size_type).name() << "\n";
    return 0;
}

Here is also an example illustrating "forced conversion", and what it entails:

 #include <iostream>
template<typename T, auto D, typename SizeType = decltype(D)>
class StaticArray
{
public:
    using size_type = SizeType;
    static const size_type convertedDim = static_cast<size_type>(D);
    static const decltype(D) Dim = D;
};

int main()
{
    StaticArray<float, 1ull<<40, int> s;
    std::cout << typeid(decltype(s.Dim)).name() << "\n";
    std::cout << s.Dim << "\n";
    std::cout << typeid(decltype(s.convertedDim)).name() << "\n";
    std::cout << s.convertedDim << "\n";
    return 0;
}
lightxbulb
  • 1,251
  • 12
  • 29