make_shared не совпадает с enable_shared_from_this? - PullRequest
0 голосов
/ 05 сентября 2018

Рассмотрим следующие два фрагмента кода: Первый:

#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 первая версия работает как задумано, а вторая не компилируется, выплевывая ошибки, такие как «неполный тип не разрешен tcp_async».

Мой вопрос:

  1. Это потому, что std :: make_shared не совпадает с std: std :: enable_shared_from_this?
  2. Или это потому, что предположения asio о том, как реализованы std :: make_shared или std :: enable_shared_from_this, не поддерживаются MSVC 2017.
  3. Или это что-то еще?

1 Ответ

0 голосов
/ 05 сентября 2018

Проблема в коде, который вы показываете, связана с тем, что конструктор вашего типа является приватным.

Когда вы пишете new tcp_connection(io_service), конструктор упоминается в самой области действия tcp_connection, которая имеет доступ.

Однако std::make_shared (или какие-либо подробности реализации, которые он может использовать) не имеет доступа к закрытому конструктору, поэтому он не может инициализировать объект, для которого предназначен общий указатель.

Если инициализация правильно сформирована, std::make_shared прекрасно работает с std::enable_shared_from_this, но закрытый конструктор делает ее некорректной.

Обходной путь для решения этой проблемы - использование идиома Passkey . Который сводится к общедоступному 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 - Я немного изменил определение вашего класса, чтобы другим было проще скопировать, вставить и протестировать его. Но тот же принцип может быть применен к вашему коду.

...