12

我正在通过 Herb Sutter's

旅程:迈向更强大、更简单的 C++ 编程

结构绑定部分

为了理解这个概念。最好是写一个我试过但得到一些错误的程序

只是想尝试如何在具有私有数据的类上使用结构绑定。请忽略以下示例。如果您可以提供任何示例

#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;
}

错误

if constexpr(I == 0) return x._ival;//'_ival' 是 'foobar' 的私有成员

else if constexpr(I == 1) return xs;//'s' 是 'foobar' 的私有成员

auto [ n, s] = f( 1024,"hello" );//不能分解非公开

需要帮助

1.如果有人可以详细说明他在这些方面实际尝试做什么(请参阅提供的链接)

// 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.任何建议如何解决上述示例的错误

4

2 回答 2

12

这里有很多问题。

首先,为了符合结构化绑定的资格,您需要专门化tuple_size

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

接下来,您的专业tuple_element还必须是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; };
}

接下来,如果您要访问私有成员,则必须像往常一样将您get声明为函数:friend

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

最后,get()真的有更好的返回引用,否则你的绑定最终会做出令人惊讶的事情:

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

与其处理friendship,不如只创建get()一个公共成员,然后编写您需要的三个重载:

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);
    }
};

同样ival()作为一个功能没有意义。您的构造函数应该只接受参数。

于 2017-08-26T19:25:44.557 回答
4

修复 Sutter 示例中的错误

我认为这是 Herb Sutter 博客文章中的一个错字/故障:他应该将这些成员公开,或者为他们提供 getter,或者让这个std::get()函数成为朋友。

此外,Herb 似乎忘记在函数签名中添加“x”......

get函数的解释

您引用的函数类似于std::get()元组的工作方式。如果我有

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

然后

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

在 Herb 的示例中,他需要对S类进行相同的工作,即std::get<0>(s)返回 的第一个成员sstd::get<1>(s)返回第二个成员等。这是必要的,因为否则,您不能S用于初始化结构化绑定。

Hebr 实现中的“魔力”在于他从函数的不同点返回不同类型的值。这种“魔法”是if constexpr. 从本质上讲,这意味着编译器会忽略除不相关分支的语法之外的所有内容。所以对于I = 0,函数为:

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;
   */
}

因为I = 1

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; */
   }
}

等,并auto选择合适的类型。

于 2017-08-26T19:22:25.310 回答