I am going through the Herb Sutter's
A journey: Toward more powerful and simpler C++ programming
Structure Binding section
In order to understand the concept .Best is to write a program I tried but getting some error
Just want to try how to use structure binding on class with private data .Please ignore the below example.if any example you can provide
#include<iostream>
#include<string>
using namespace std;
class foobar {
public:
foobar() { cout << "foobar::foobar()\n"; }
~foobar() { cout << "foobar::~foobar()\n"; }
foobar( const foobar &rhs )
{ cout << "foobar::foobar( const foobar & )\n"; }
void ival( int nval, string new_string ) { _ival = nval;s=new_string; }
private:
int _ival;
string s;
};
foobar f( int val,string new_string ) {
foobar local;
local.ival( val,new_string );
return local;
}
template<> struct tuple_element<0,foobar> { using type = int; };
template<> struct tuple_element<1,foobar> { using type = string; };
// 2. Now add get<> support (using C++17, because why not; it’s better
// than =delete’ing the primary get<> template and adding specializations)
template<int I>
auto get(const foobar&x) {
if constexpr(I == 0) return x._ival;//'_ival' is a private member of 'foobar'
else if constexpr(I == 1) return x.s;//'s' is a private member of 'foobar'
}
int main(){
foobar ml = f( 1024,"hello" );
auto [ n, s] = f( 1024,"hello" );//Cannot decompose non-public member '_ival' o
return 0;
}
Error
if constexpr(I == 0) return x._ival;//'_ival' is a private member of 'foobar'
else if constexpr(I == 1) return x.s;//'s' is a private member of 'foobar'
auto [ n, s] = f( 1024,"hello" );//Cannot decompose non-public
Help required
1.If anyone can elaborate what he is actually trying to do on these lines (please refer the link provided)
// 2. Now add get<> support (using C++17, because why not; it’s better
// than =delete’ing the primary get<> template and adding specializations)
template<int I>
auto get(const S&) {
if constexpr(I == 0) return x.i;
else if constexpr(I == 1) return string_view{x.c}; }
else if constexpr(I == 2) return x.d;
}
2.Any suggestion how to fix the error for the above example
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 friend
ship, 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.
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...
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With