Не удается преобразовать из std::shared_ptr‹_Ty› в std::shared_ptr‹_Ty›

Я получаю следующую ошибку:

ошибка C2440: 'static_cast': невозможно преобразовать из 'std::shared_ptr‹_Ty>' в 'std::shared_ptr‹_Ty> stack\genericstack.h 36 1 Stack

GenericStack.h

#ifndef _GENERIC_STACK_TROFIMOV_H_
#define _GENERIC_STACK_TROFIMOV_H_

#include <memory>

class GenericStack {
    struct StackNode {
        std::shared_ptr<void> _data; 
        StackNode* _next;
        StackNode(const std::shared_ptr<void>& data, StackNode* next) 
            : _data(data), _next(next) {

        }
    };

    StackNode* _top; 

    GenericStack(const GenericStack&);
    GenericStack& operator=(const GenericStack&);

protected:
    GenericStack();
    ~GenericStack();
    void push(const std::shared_ptr<void>&);
    void pop();
    std::shared_ptr<void>& top();
    bool isEmpty() const;
};

template <class T>
class TStack: private GenericStack {                  
public:
    void push(const std::shared_ptr<T>& p) { GenericStack::push(p); }
    void pop() { GenericStack::pop(); }
    std::shared_ptr<T> top() { return static_cast<std::shared_ptr<T>>(GenericStack::top()); }
    bool empty() const { return GenericStack::isEmpty(); }
};

#endif

GenerickStack.cpp

#include "GenericStack.h"

GenericStack::GenericStack()
    :_top(0) {

};
GenericStack::~GenericStack() {
    while(!isEmpty()) {
        pop();
    }
};

void GenericStack::push(const std::shared_ptr<void>& element) {
    _top = new StackNode(element, _top);
}

std::shared_ptr<void>& GenericStack::top() {
    return _top->_data;
}
void GenericStack::pop() {
    StackNode* t = _top->_next;
    delete _top;
    _top = t;
}

bool GenericStack::isEmpty() const {
    return !_top;
}

Main.cpp

#include <iostream>
#include "GenericStack.h"

int main() {
    TStack<int> gs;

    std::shared_ptr<int> sh(new int(7));
    gs.push(sh);
    std::cout << *gs.top() << std::endl;

    return 0;
}

Почему я получаю сообщение об ошибке?

Я ожидаю, что приведение произойдет успешно, поскольку с необработанными указателями я всегда могу регистрировать от void* до указателя реального типа.

Здесь я хочу создать шаблон стека. Но я стараюсь повторно использовать как можно больше кода, чтобы шаблонные классы не слишком раздувались.

Спасибо.


person yourbuddy    schedule 25.11.2018    source источник
comment
Я ожидаю, что приведение произойдет успешно, поскольку с необработанными указателями я всегда могу регистрировать от void* до указателя реального типа. Потому что std::shared_ptr сохраняет безопасность типов, а вы пытаетесь отбросить это. Также имейте в виду, что успешно защитит вас только от вопиющих ошибок компилятора и не защитит от неопределенного поведения.   -  person πάντα ῥεῖ    schedule 25.11.2018
comment
@πάνταῥεῖ, разве невозможно то, что я пытаюсь сделать?   -  person yourbuddy    schedule 25.11.2018
comment
невозможно ли то, что я пытаюсь сделать? Зависит от того, как эти типы соотносятся друг с другом.   -  person πάντα ῥεῖ    schedule 25.11.2018
comment
Придирка (не относящаяся к вашему вопросу, но технически важная): использование идентификатора _GENERIC_STACK_TROFIMOV_H_. Формально это зарезервированный идентификатор.   -  person curiousguy    schedule 25.11.2018
comment
@πάνταῥεῖ, они не связаны, но каждый раз, когда выполняется приведение, гарантируется, что ранее вместо shared_ptr<void> было поставлено shared_ptr<T>.   -  person yourbuddy    schedule 25.11.2018
comment
@curiousguy, я думаю, что идентификатор нигде не будет использоваться, но спасибо, я понимаю проблему, на которую вы указываете.   -  person yourbuddy    schedule 25.11.2018
comment
static_cast 1) не является перегружаемым оператором 2) для пользовательских типов: может делать только то, что может сделать вызов конструктора   -  person curiousguy    schedule 25.11.2018


Ответы (2)


Вы получаете эту ошибку, потому что static_cast требует, чтобы типы from и to были конвертируемыми. Для shared_ptr это будет действовать, только если будет участвовать перегрузка c'tor 9 в разрешении перегрузки. Но это не так, поскольку void* не может быть неявно преобразован в другие типы указателей объектов в C++, ему нужен явный static_cast.

Если вы хотите преобразовать общие указатели на основе static_casting типов управляемых указателей, вам необходимо использовать std::static_pointer_cast, для этого он и нужен.

Итак, после подключения этого исправления

 std::shared_ptr<T> top() { return std::static_pointer_cast<T>(GenericStack::top()); }

Ваша тонкая оболочка шаблона будет работать нормально.

person StoryTeller - Unslander Monica    schedule 25.11.2018
comment
Не могли бы вы помочь мне с другим вопросом в чате? - person yourbuddy; 25.11.2018
comment
@yourbuddy - Если у вас есть другая проблема, возможно, стоит задать еще один вопрос. - person StoryTeller - Unslander Monica; 25.11.2018
comment
Вот. Спасибо. - person yourbuddy; 25.11.2018

Взгляните на список конструкторов для shared_ptr. Вы пытаетесь использовать перегрузку 9, точнее перегрузку шаблона с Y = void и T = int. Однако эта перегрузка шаблона не участвует в разрешении перегрузки, поскольку void* нельзя неявно преобразовать в int*. Другими словами, вы не можете даже явно преобразовать shared_ptr<void> в shared_ptr<T>, если вы не можете неявно преобразовать void* в T*.

Почему бы не использовать шаблон в первую очередь (переместить GenericStack функциональность в TStack) вместо того, чтобы пытаться работать с void*?

Но я стараюсь повторно использовать как можно больше кода, чтобы шаблонные классы не слишком раздувались.

Я предполагаю, что под «набуханием» вы подразумеваете, что шаблонное решение будет генерировать слишком много экземпляров? Есть ли у вас основания полагать, что их действительно будет слишком много?

person Nelfeal    schedule 25.11.2018