Structured bindings for your own type that isn’t a struct or a tuple(via public member function)

There are many problems here.

First, in order to qualify for structured bindings, you need to specialize tuple_size:

namespace std {
    template <> struct tuple_size<foobar> : std::integral_constant<size_t, 2> { };
}

Next, your specializations of tuple_element also have to be in namespace std:

namespace std {
    template <> struct tuple_size<foobar> : std::integral_constant<size_t, 2> { };

    template <> struct tuple_element<0,foobar> { using type = int; };
    template <> struct tuple_element<1,foobar> { using type = std::string; };
}

Next, your get must be declared as a friend function if you're going to access private members, as per usual:

class foobar {
    template <int I> friend auto get(foobar const& );
};

Lastly, get() really had better return references, otherwise your bindings will end up doing surprising things:

template<int I>
auto const& get(const foobar&x) {
    if      constexpr(I == 0) return x._ival;
    else if constexpr(I == 1) return x.s;
}

Rather than dealing with friendship, it's easier to just make get() a public member, and then write the three overloads you need:

class foobar {
public:
    template <size_t I>
    auto& get() & {
        if constexpr (I == 0) return _ival;
        else if constexpr (I == 1) return s;
    }

    template <size_t I>
    auto const& get() const& {
        if constexpr (I == 0) return _ival;
        else if constexpr (I == 1) return s;
    }

    template <size_t I>
    auto&& get() && {
        if constexpr (I == 0) return std::move(_ival);
        else if constexpr (I == 1) return std::move(s);
    }
};

Also ival() as a function doesn't make sense. Your constructor should just take arguments.


Fixing the errors in Sutter's example

I think it's a typo/glitch in Herb Sutter's blog post: He should have made those members public, or provided getters for them, or made the std::get() function a friend.

Also, it looks like Herb forgot to put "x" in the function signature...

Explanation of the get function

The function you quote is similar to how std::get() works for tuples. If I have

std::tuple<int, std::string> t;

then

auto x { std::get<0>(t) }; // x is an integer
auto y { std::get<1>(t) }; // y is an std::string

and in Herb's example, he needs to have the same work for the S class, i.e. have std::get<0>(s) return the first member of s, std::get<1>(s) return the second member etc. This is necessary, because otherwise, you can't use S for initializing a structured binding.

The "magic" in Hebr's implementation is that he's returning values of different types from different points in his function. This "magic" is the effect of an if constexpr. It means, essentially, that the compiler ignores everything except the syntax of the irrelevant branches. So for I = 0, the function is:

auto get(const S&) {
  if (true) return x.i;
  /* else if constexpr(I == 1) return string_view{x.c}; 
     else if constexpr(I == 2) return x.d;
   */
}

for I = 1 it's

template<int I>
auto get(const S&) {
   if      (false) {/* return x.i; */ ; }
   else if (true) return string_view{x.c};
   /* else if constexpr(I == 2) return x.d; */
   }
}

etc. And the auto chooses the appropriate type.