make_shared 不和 enable_shared_from_this 一起玩?
make_shared doesn't play along with enable_shared_from_this?
考虑以下两个代码片段,
第一个:
#include "pch.h"
#include <memory>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class tcp_connection : public std::enable_shared_from_this<tcp_connection>
{
public:
typedef std::shared_ptr<tcp_connection> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new tcp_connection(io_service));
//second example only differs by replacing the above line with the below one
//return std::make_shared<tcp_connection>(io_service);
}
private:
tcp_connection(boost::asio::io_service& io_service) //private constructor
: socket_(io_service)
{
}
tcp::socket socket_;
};
int main()
{
return 0;
}
第二个和第一个只有一行不同,也就是注释的那一行
对于 MSVC 2017 和 boost::asio 1.68,第一个版本按预期工作,而第二个版本无法编译,并出现 "incomplete type is not allowed tcp_async".
等错误
我的问题是:
- 这是因为std::make_shared不跟std:std::enable_shared_from_this一起玩吗?
- 或者,这是因为 asio 持有关于如何实现 std::make_shared 或 std::enable_shared_from_this 的假设,不适用于 MSVC 2017。
- 还是其他原因?
您显示的代码中的问题源于您的类型的构造函数是私有的。
当您编写 new tcp_connection(io_service)
时,构造函数在 tcp_connection
本身的范围内被引用,它具有访问权限。
但是,std::make_shared
(或它可能使用的任何实现细节)无法访问私有构造函数,因此它无法初始化它应该由共享指针管理的对象。
如果初始化是良构的,std::make_shared
与 std::enable_shared_from_this
一起工作得很好,但私有构造函数会使它成为病态的。
一个常见的解决方法是使用 Passkey idiom。归结为 public c'tor,但它接受私有类型的参数。它有点像这样1:
class tcp_connection2: public std::enable_shared_from_this<tcp_connection2>
{
struct key{ explicit key(){} };
public:
typedef std::shared_ptr<tcp_connection2> pointer;
static pointer create(int io_service)
{
return std::make_shared<tcp_connection2>(io_service, key{});
}
tcp_connection2(int io_service, key) //private constructor, due to key
{
}
};
1 - 我稍微修改了您的 class 定义,以便其他人更容易复制、粘贴和测试它。但同样的原则也适用于您的代码。
考虑以下两个代码片段, 第一个:
#include "pch.h"
#include <memory>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class tcp_connection : public std::enable_shared_from_this<tcp_connection>
{
public:
typedef std::shared_ptr<tcp_connection> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new tcp_connection(io_service));
//second example only differs by replacing the above line with the below one
//return std::make_shared<tcp_connection>(io_service);
}
private:
tcp_connection(boost::asio::io_service& io_service) //private constructor
: socket_(io_service)
{
}
tcp::socket socket_;
};
int main()
{
return 0;
}
第二个和第一个只有一行不同,也就是注释的那一行
对于 MSVC 2017 和 boost::asio 1.68,第一个版本按预期工作,而第二个版本无法编译,并出现 "incomplete type is not allowed tcp_async".
等错误我的问题是:
- 这是因为std::make_shared不跟std:std::enable_shared_from_this一起玩吗?
- 或者,这是因为 asio 持有关于如何实现 std::make_shared 或 std::enable_shared_from_this 的假设,不适用于 MSVC 2017。
- 还是其他原因?
您显示的代码中的问题源于您的类型的构造函数是私有的。
当您编写 new tcp_connection(io_service)
时,构造函数在 tcp_connection
本身的范围内被引用,它具有访问权限。
但是,std::make_shared
(或它可能使用的任何实现细节)无法访问私有构造函数,因此它无法初始化它应该由共享指针管理的对象。
如果初始化是良构的,std::make_shared
与 std::enable_shared_from_this
一起工作得很好,但私有构造函数会使它成为病态的。
一个常见的解决方法是使用 Passkey idiom。归结为 public c'tor,但它接受私有类型的参数。它有点像这样1:
class tcp_connection2: public std::enable_shared_from_this<tcp_connection2>
{
struct key{ explicit key(){} };
public:
typedef std::shared_ptr<tcp_connection2> pointer;
static pointer create(int io_service)
{
return std::make_shared<tcp_connection2>(io_service, key{});
}
tcp_connection2(int io_service, key) //private constructor, due to key
{
}
};
1 - 我稍微修改了您的 class 定义,以便其他人更容易复制、粘贴和测试它。但同样的原则也适用于您的代码。