2

I see a few similar questions, but they don't seem to get at this.

I can overload a function on a template:

template <typename T> void foo(); // Called as e.g., foo<int>();
template <std::size_t I> void foo(); // Called as e.g., foo<2>();

Is there a way to do something similar with a class, where I can have either MyClass<int> or MyClass<2>?

I can't see a way to do it. (My real goal is to have a static constexpr instance of an invokable that acts like std::get. Whereas std::get is templated on its "indexing" parameter and on the argument type, I'd like to make one that is invokable on its own, so myns::get<2> and myns::get<int> are both invokable and when invoked act like std::get<2>(tup) and std::get<int>(tup) respectively.

Naively you'd hope something like this would work, but the auto is a type:

template <auto IndOrType>
constexpr auto get = [](auto&& tup) {
    return std::get<IndOrType>(std::forward<decltype(tup)>(tup));
};

I can make a getter function that returns a lambda, but then it's called with () as in myns::get<2>()(tup) or myns::get<int>()(tup).

Ben
  • 9,184
  • 1
  • 43
  • 56

1 Answers1

1

You could specialize the class using the helper type std::integral_constant<int,N>. Though unfortunately this doesn't exactly allow the MyClass<2> syntax:

#include <type_traits>

template<typename T>
class MyClass
{
};

template<int N>
using ic = std::integral_constant<int,N>;

template<int N>
class MyClass<ic<N>>
{

};

int main()
{
    MyClass<int> a;
    MyClass<ic<2>> b;
}

https://godbolt.org/z/84xoE1Yd4

Marc Stevens
  • 1,628
  • 1
  • 6
  • 16
  • Right. If only there were a way to make `2` implicitly convert to `ic<2>` in that context... – Ben Dec 06 '21 at 19:59
  • Yeah, I was hoping it might automatically match against the specialization, but alas. – Marc Stevens Dec 06 '21 at 20:00
  • BTW I'm not entirely sure of your intended use case. But couldn't perhaps an overloaded function `makeMyClass` work? Instead of directly naming the type, let a overloaded function do the specialization to either version of MyClass. – Marc Stevens Dec 06 '21 at 20:03
  • I was really just hoping to syntactically do `auto f = get; f(t);` and have that act like `std::get(t);`, which seems natural. It seems so simple, yet to do that, I found myself feeling like it might be possible, so I figured it was a good question for SO. – Ben Dec 06 '21 at 20:13
  • It seems that function templates are the only thing that can be overloaded on leading template parameter, so it may ultimately not be possible to overload like this. – Ben Dec 06 '21 at 20:22
  • You could still do like `std::get`, but add a third overload: `get(void)` that returns the lambda you mentioned. Then you have the regular use of `get`, as well as `auto f = myns::get(); f(t);`. – Marc Stevens Dec 06 '21 at 20:42
  • Yeah, short of a way to put either a type or a non-type template parameter in the same place, I think that's the best solution. – Ben Dec 07 '21 at 17:56
  • I just saw this SO answer pointing to a proposal to add truly universal template parameters. I think that would solve this: https://stackoverflow.com/a/70377784/874660 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1985r0.pdf – Ben Dec 17 '21 at 02:46
  • OK, P1985 does address this. Here's it working in the Circle C++ compiler: https://godbolt.org/z/cfr4jjTcz – Ben Dec 17 '21 at 12:47