3

I've been stumped on this for a while, and distilled a simple example of my error message. Is there some way to make this work? Am I just missing a "template" or "typename" somewhere?

#include <cstdint>

template<typename T, typename J>
struct silly
{
    const void (*f)(T,J);
};

template<typename T, typename J, silly<T, J> aSilly>
struct sillier
{
    const uint32_t something;
};

void dumb_func(uint32_t i, uint32_t j)
{
    return;
}

constexpr silly<uint32_t, uint32_t> mySilly{ .f = dumb_func };

using silliest = sillier<uint32_t, uint32_t, mySilly>;

int main()
{
    return 2;
}

g++ spits out:

g++ -std=c++2a ugh.cpp

ugh.cpp:20:51: error: invalid conversion from ‘void (*)(uint32_t, uint32_t)’ {aka ‘void (*)(unsigned int, unsigned int)’} to ‘const void (*)(unsigned int, unsigned int)’ [-fpermissive]
   20 | constexpr silly<uint32_t, uint32_t> mySilly{ .f = dumb_func };
      |                                                   ^~~~~~~~~
      |                                                   |
      |                                                   void (*)(uint32_t, uint32_t) {aka void (*)(unsigned int, unsigned int)}
ugh.cpp:20:51: error: ‘dumb_func’ is not a valid template argument of type ‘const void (*)(unsigned int, unsigned int)’ because ‘dumb_func’ is not a variable

I've tried reading through https://en.cppreference.com/w/cpp/language/template_parameters but I'm out of my template depth here. The actual use case for this has T and J being something like std::array<float, ?> and then sillier<T, J, silly<T, J>> inherits from some other more general class, and has overrides which call functions like f so that everything gets inlined into specialized functions for a given instance of silly<T, J>.

cigien
  • 57,834
  • 11
  • 73
  • 112
Sugario
  • 33
  • 3

1 Answers1

4

Edit:

The third template parameter of sillier is a non-type template argument, which can only be bound to a variable, but dumb_func is not a variable.

This explanation doesn't make sense, and in fact the code is probably fine, and the error might just be a bug as discovered and reported in this answer. The fix given below still works though.


You can make the third template parameter be a non-type template parameter of reference type.

template<typename T, typename J, silly<T, J> const & aSilly>
                                          // ^^^^^^^
struct sillier
{
    const uint32_t something;
};

Also, the member variable f in silly is declared as a function pointer returning a const void type:

const void (*f)(T,J);

So you either need to remove the const from the return type of f, or you can change the declaration of dumb_func to return a const void type:

const void dumb_func(uint32_t i, uint32_t j)
{
    return;
}

There doesn't seem to be any point in a const void returning function, so I would go with the first option.


Here's a demo.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • <3 <3 <3 Many thanks. Yeah I wanted that the be a const pointer, in the actual code I'm doing a "using FunctionType = std::add_pointer_t>" type of thing so I didn't pay attentiong to the associativity (or whatever you'd call that) of the type declarations – Sugario Nov 03 '20 at 00:19
  • @AlexanderSugar No problem :) I see why you did that now. Yes, putting const in the right place can be tricky, it confuses me all the time :p Try [cdecl.org](https://cdecl.org/), it's helpful in these cases. – cigien Nov 03 '20 at 00:23
  • Though I'm actually still confused: you say the third parameter is a non-type template argument and so we cannot bind dumb_func to it, but I wasn't trying to bind dumb_func, I was trying to bind mySilly, right? This is also what confused me about the error message... Does this have to do with parameter vs argument? – Sugario Nov 03 '20 at 00:23
  • @AlexanderSugar Hmm, you're right, the fix is correct, but the explanation is not clear. I'll have to think about exactly what's happening here. You should unaccept the answer, otherwise other users who may know the answer might not look at the question. – cigien Nov 03 '20 at 00:27
  • 1
    @AlexanderSugar Ok, now I'm not even sure if my fix is necessary. clang is happy to accept your code :( (after you fix the `const` issues with `f`). So it *might* be a bug with gcc/clang. The gcc diagnostic is a bit strange. – cigien Nov 03 '20 at 00:29
  • Putting my code into clang with the const moved inside, I get an error saying that a non type template parameter cannot have type silly. – Sugario Nov 03 '20 at 00:39
  • @AlexanderSugar That's strange. Are you sure you're compiling with c++20? – cigien Nov 03 '20 at 00:40
  • 1
    @AlexanderSugar Ah, I see. clang 11.0 doesn't support that :) Just compile with trunk. – cigien Nov 03 '20 at 00:43
  • oh wow ok, so yeah might actually be a bug. I guess const& template parameter it is for now. – Sugario Nov 03 '20 at 00:50
  • @AlexanderSugar Yeah, sounds good :) I plan on writing a language-lawyer question if I can't figure it out, and I'll link to this question if I do that. And if this *does* answer your question (since you only seem to want a fix), you can accept the answer :) – cigien Nov 03 '20 at 00:52
  • @AlexanderSugar I posted a question [here](https://stackoverflow.com/questions/64655958). – cigien Nov 03 '20 at 02:25
  • Cool, thanks again. I'll check back in a day or two and see what's what. I will say though that it seems to me that your question as asked is answered by https://en.cppreference.com/w/cpp/language/template_parameters, specifically the definition of a structural type and the C++20 bit about literal class types. – Sugario Nov 03 '20 at 02:35
  • @AlexanderSugar Ok, but in which way? Is it valid or not? Also, could you answer/comment on that question instead? – cigien Nov 03 '20 at 02:37