6

I think code below is self explanatory. I can easily pass static variable to template parameter and it work as expected. Using static array will cleanup code, so it look nicer, but unfortunately it doesn't compile due to error I pasted in comment. Note that it was compiled by gcc 10.2 with c++17 flag. So the question is how to pass array element to template.

#include <iostream>
#include <vector>
#include <tuple>

using DataTransfer = std::tuple<char, int>;
using DataPool     = std::vector<DataTransfer>;

typedef struct Event
{
    DataPool dataPool;
    const char* description;
} Event;

template <Event& event>
class EventTransmitter
{
    public:
    EventTransmitter()
    {
        std::cout<<event.description<<"\n";
    }
};

static Event ev1{ {{'d', 4}, {'a', 1}}, "Description 1"};
static Event ev2{ {{'g', 7}, {'b', 6}}, "Description 2"};

static Event evs[2] {
    { {{'d', 4}, {'a', 1}}, "Description 1"},
    { {{'g', 7}, {'b', 6}}, "Description 2"}
};

int main()
{
    EventTransmitter<ev1> e1;
    EventTransmitter<ev2> e2;
    
    //EventTransmitter<evs[0]> e3;
    //error: '& evs[0]' is not a valid template argument of
    //type 'Event&' because 'evs[0]' is not a variable
    return 0;
}  
docp
  • 307
  • 1
  • 9
  • 2
    Why do you create a new class out of the template with every object? I didn't know that this is possible. Why don't you just pass the object to the constructor? You wouldn't even need a template. Always write programs in the least surprising way and your code is very surprising. – mch Oct 28 '20 at 12:43
  • gcc (trunk) compiles the code with `-std=c++20` and gives the expected output, but not with `-std=c++17`. clang gives probably a better error message: `non-type template argument refers to subobject 'evs[0]'` https://godbolt.org/z/6exajh – mch Oct 28 '20 at 12:48
  • @mch Let's say you docp wants event handlers which can only handle specific events. Having seperate types for each handler allows compile errors when binding the wrong handler to the wrong event. Just one possibility of why you would want to use code such as this. – AVH Oct 28 '20 at 12:54
  • @mch This is just very simplified code so it readable. In reality there is many many more things that forced me to do like in code above. – docp Oct 28 '20 at 12:59

2 Answers2

4

TL;DR upgrade your compiler, and hope that they fully implement C++20.


The problem is purely one about non-type template parameters

template<int&>
struct S;

static int i;
static int arr[42];

S<i> s1;
S<arr[0]> s2;  // ill-formed?

The static is also irrelevant, in case you're wondering.

This rule exist in C++17 [temp.arg.nontype]

For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject, [...]

Which got relaxed in C++20

For a non-type template-parameter of reference or pointer type, [...] the reference or pointer value shall not refer to or be the address of (respectively):

  • a temporary object,
  • a string literal object,
  • the result of a typeid expression,
  • a predefined __func__ variable, or
  • a subobject of one of the above.

As to why, I can only postulate that the standard cautiously only required a very small subset of values to avoid the possibility that it isn't implementable.

Passer By
  • 19,325
  • 6
  • 49
  • 96
  • There were two reasons to avoid such template arguments: needing to mangle the "path" through the object to its subobject, and some longstanding confusion about the meaning of `==` for template arguments. Implementers decided that the first wasn't too big a deal and the committee [gradually recovered from the latter](https://stackoverflow.com/a/63714924/8586227). – Davis Herring May 30 '21 at 14:46
2

There were an answer here (which was deleted) that gave me an idea how to solve it. It's not perfect, but also not bad.

#include <iostream>
#include <vector>
#include <tuple>

using DataTransfer = std::tuple<char, int>;
using DataPool     = std::vector<DataTransfer>;

typedef struct Event
{
    DataPool dataPool;
    const char* description;
} Event;

template <Event* event, int index>
class EventTransmitter
{
    public:
    EventTransmitter()
    {
        std::cout<<(event+index)->description<<"\n";
    }
};

static Event ev1{ {{'d', 4}, {'a', 1}}, "Description 1"};
static Event ev2{ {{'g', 7}, {'b', 6}}, "Description 2"};

static Event evs[2] {
    { {{'d', 4}, {'a', 1}}, "Description 1"},
    { {{'g', 7}, {'b', 6}}, "Description 2"}
};

int main()
{
    //EventTransmitter<&ev1> e1;
    //EventTransmitter<&ev2> e2;
    
    EventTransmitter<evs, 0> e3;
    EventTransmitter<evs, 1> e4;

    return 0;
}  
docp
  • 307
  • 1
  • 9