назначить std :: unique_ptr для std :: function - PullRequest
0 голосов
/ 29 января 2019

Существует пользовательская карта с элементом std :: function ()>.Лямбда-код работает, но я не знаю, как расширить его до нормального формирования.Код следующий.

class TestA{
public:
    TestA() {}
    ~TestA() {}

    TestA(const TestA &) {}

    static void print()
    {
        cout << __FUNCTION__ << endl;
        return;
    }
};

void testComplexMap1()
{

    typedef map<string, std::function<std::unique_ptr<TestA>()>> TempMap;
    TempMap m;
    // the lambda format code, it works
    //m.insert({ "TestA", []() {return std::unique_ptr<TestA>(new TestA());}});

    // I want to expand it, but failed.
    TestA *t = new TestA();
    //function<unique_ptr<TestA>()> fp(unique_ptr<TestA>(t));
    function<unique_ptr<TestA>()> fp(unique_ptr<TestA>(t)()); //warning here
    //m.emplace("TestA", fp);              // compile error here
}

Любая помощь будет принята с благодарностью.

Ответы [ 2 ]

0 голосов
/ 29 января 2019

В C ++ все, что выглядит как объявление, рассматривается как таковое.Это означает, что строка

function<unique_ptr<TestA>()> fp(unique_ptr<TestA>(t)());

интерпретируется как: fp - это объявление функции, возвращающей std::function<unique_ptr<TestA>()> и ожидающей параметр с именем t, который является указателем на функцию, возвращающую std::unique_ptr<TestA> и не получая параметров.(Это не то, что вы намеревались.)

Это также означает, что t в этой строке не является t, как в предыдущей строке.

Вы должны пройти fp что-то, что на самом деле можно вызвать, например:

std::unique_ptr<TestA> f() {    
    return std::make_unique<TestA>();
}

void testComplexMap1() {
    // ...
    function<unique_ptr<TestA>()> fp(f);
    m.emplace("TestA1", fp);  
}

Если вы хотите добавить на карту функцию, которая оборачивает существующий указатель в unique_ptr, вам понадобится либо функтор:

class Functor {
public:
    Functor(TestA * a) : m_a(a) {}
    ~Functor() { delete m_a; }

    std::unique_ptr<TestA> operator()(){
        auto x = std::unique_ptr<TestA>(m_a);
        m_a = nullptr;
        return std::move(x);
    }

private:
    TestA * m_a;
};

void testComplexMap1() {
    //...
    TestA * t = new TestA();
    m.emplace("TestA", Functor(t));  
}

Или лямбда с захватом:

void testComplexMap1() {
    //...
    TestA * t = new TestA();
    m.emplace("TestA", [t](){ return std::unique_ptr<TestA>(t); });  
}

Лямда более или менее переводится в нечто вроде Functor класса.Однако в каждом случае вы должны быть очень осторожны: функции на карте, которые инкапсулируют существующий указатель в std::unique_ptr, могут и должны вызываться только один раз.

Если вы не вызываете их, выделяется памятьдля t не будет освобожден.Если вы вызываете их более одного раза, вы получаете либо std::unique_ptr - nullptr (в моем варианте Functor класса), либо более одного std::unique_ptr пытается управлять одной и той же областью памяти (в лямбда-режиме с вариантом захвата), которая вылетает, как только удаляется вторая std::unique_ptr.

Короче говоря: я бы посоветовал не писать такой код и ставить на карту только те функции, которые можно вызывать несколько раз.

0 голосов
/ 29 января 2019

fp не инициализируется с помощью функции, поэтому компиляция не удалась.

Вы можете развернуть ее следующим образом:

TestA *t = new TestA();
std::unique_ptr<TestA> UT(t);   
auto func = [&]() { return move(UT);};
std::function<std::unique_ptr<TestA>()> fp(func);
m.emplace("TestA", fp);

См. DEMO .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...