13

What are the templates that I have to specialize to support std::get?

struct MyClass {
  int a;
};

template <const size_t I>
struct MyContainer {
  MyClass array[I];
};

What do I have to specialize to be able to do:

MyContainer<16> mc;
std::get<0>(mc);
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
Matt Clarkson
  • 14,106
  • 10
  • 57
  • 85
  • I think `std::get(...)` is really intended to be applied to only `std::tuple` and that it would be better to define your own function for your custom class. – Timothy Shields Apr 23 '13 at 15:57
  • 1
    I'm not sure how much of this has changed, but currently, `std::get` is defined for array/span/pair/tuple/variant within their (static) sizes. Before we tackle support for MyContainer, I think it's worth asking why there isn't support for statically-sized arrays like the 'array' member. Are there more conditions to qualify for a `std::get`, e.g. the argument must be an STL container? Then it would be more clear that MyContainer needs a get method that *isn't* `std::get`. I'm still interested in this question since `std::get` isn't the only one. – John P Jul 13 '21 at 02:38

2 Answers2

19

std::get is not a customization point for the standard library; the three function template overloads (for pair, tuple and array) do not explicitly allow for user-defined overloads, so 17.6.4.2.1p1 applies and adding a declaration of your own function template overload is undefined behaviour.

Note that get as an unqualified name is a customization point as of C++17; it is used by the structured binding declaration protocol to access tuple-like elements; but this is as an unqualified name, not the qualified name std::get.

That said, if you were to write:

namespace std {
   template<size_t I, size_t N> MyClass &get(MyContainer<N> &c) { return c.array[I]; }
}

and similarly for the rvalue reference and const reference overloads, your program would likely work as you expect.

However, there's little point seeing as the standard already supplies array:

template<size_t N> using MyContainer = std::array<MyClass, N>;
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Thanks. _`std::get` is not a customization point_ is what I was wondering really. – Matt Clarkson Apr 24 '13 at 13:32
  • 3
    Doesn't this change in C++17, so that users can make their own classes work with structured bindings, by leveraging lookup of _"`get(e)`, where get is looked up by argument-dependent lookup"_? See [cppreference](http://en.cppreference.com/w/cpp/language/structured_binding), Case 2 – underscore_d Oct 23 '17 at 02:36
  • 2
    @underscore_d not quite; that's `get` as an unqualified name, not the qualified name `std::get`. It's an important point though; thanks! – ecatmur Oct 23 '17 at 07:50
  • 5
    @underscore_d the problem with that is that get<0>(e) doesn't use argument-dependent lookup because the <0> blocks ADL from working. (See https://godbolt.org/z/YIEa-c This is fixed in C++20, as you can see by changing c++17 to c++2a in that godbolt link.) – jorgbrown Jun 04 '19 at 22:38
  • If you read the item of the standard that was mentioned, it says that _explicit_ template function specializations are undefined behavior. So is it still undefined behavior to specialize get for a template type? For instance, is `template std::get>` still undefined behavior? – Josie Thompson Mar 12 '22 at 07:07
  • 1
    @JosieThompson well, you can't partially specialize function templates at all, so I think that's why the Standard doesn't bother to mention it. – ecatmur Mar 12 '22 at 16:53
  • As a workaround for C++17, where `get<>` is not found inside namespace by ADL, one can tell the compiler that `get` is a template with `using MyNamespace::get;`. – Ave Milia Jun 17 '22 at 11:01
1

I'm guessing you want to implement some algorithms that need access to arbitrary array-like containers using compile-time indices and therefor aim to use some function (like std::get) to uniformly perform that task?! In that case, it is the same business as making begin and end available for your class. You simply declare a function get in the namespace you declared your container class in, and let ADL do its jobs.

    template <unsigned I, unsigned N>
    MyClass& get (MyContainer<N>& c) { return c.array[I]; }

    template <unsigned I, unsigned N>
    MyClass const& get (MyContainer<N> const& c) { return c.array[I]; }

In your algorithm you just use get (without the std namespace prefix) and ADL will call the correct function. So, for the standard structures like array, tuple and pair std::get is invoked and for your container the get function that you provided.

    int main(){
        std::array<int, 3> a {{0,1,2}};
        auto t = std::make_tuple(0.0, 1.0f, 2);
        auto p = std::make_pair('0', 4.4);
        MyContainer<3> c;

        std::cout << get<1>(a) << std::endl;
        std::cout << get<1>(t) << std::endl;
        std::cout << get<1>(p) << std::endl;
        std::cout << get<1>(c).a << std::endl;

        return 0;
    }

Example

  • 5
    ADL doesn't work on explicit instantiation of function templates; at parse time, the name `get` isn't recognised as a template, so the angle brackets are parsed as comparison operators. (Try putting `MyContainer` in a namespace.) – ecatmur Apr 24 '13 at 13:43