c++ - 使用 std::unique_ptr 使用 CRTP 进行向上转换

标签 c++ c++11 c++14

我指的是 this并使用 CRTP 来跟踪正在创建的对象的数量。但是当我尝试将 ProfessorStudent class 转换为 Person class 时,我遇到了编译问题std::unique_ptr。请引用下面的片段,链接是here

#include <iostream>
#include <vector>
#include <memory>
#include <numeric>
#include <exception>

using namespace std;

template<typename T>
class Person
{
    public:
    Person(string Name="", int Age=0) : name(Name), age(Age) 
    {
        ++cur_id;
    }
    virtual ~Person() {}
    virtual void getdata() = 0;
    virtual void putdata() = 0;

    protected:
    string name;
    int age;
    static int cur_id;
};

template<typename T> int Person<T>::cur_id = 0;

class Professor : public Person<Professor>
{
    public:
    Professor(string Name="", int Age=0, int Publications=0);
    ~Professor() {}
    void getdata() override;
    void putdata() override;

    protected:
    bool IsValidInput();

    private:
    int publications;
};

Professor::Professor(string Name, int Age, int Publications) try : Person(Name,Age), publications(Publications)
{
}

// Catch constructor that fails
catch(std::exception & e)
{
    std::cout<<"\n Exception - "<<e.what()<<std::endl;
    exit(1);    
}

bool Professor::IsValidInput()
{
    if (name.empty() || (name.length() > 100))
    {
        return false;
    }

    if ( age <= 0 || age > 80 )
    {
        return false;  
    }

    if ( publications <= 0 || publications > 1000)
    {
        return false;
    }

    return true;
}

void Professor::getdata()
{
    cout<< "Enter Name age publications" << endl;
    cin >> name >> age >> publications;

    if (!IsValidInput())
    {
        throw std::invalid_argument("wrong arguments");
    }
}

void Professor::putdata()
{
    cout << name << " " << age << " " << publications << " " << cur_id << endl;
}

class Student : public Person<Student>
{
    public:
    Student(string Name="", int Age=0, const vector<int> & Marks = vector<int>());
    ~Student() {}

    void getdata() override;
    void putdata() override;

    protected:
    bool IsValidInput();    

    private:
    vector<int> marks;
    static const int TOTAL_MARKS = 5;
};

Student::Student(string Name, int Age, const vector<int> & Marks) try : Person(Name,Age), marks(Marks)
{
}

// Catch constructor that fails
catch(std::exception & e)
{
    std::cout<<"\n Exception - "<<e.what()<<std::endl;
    exit(1);    
}

void Student::getdata()
{
    int mark = 0;
    cout<< "Enter Name age marks(5)" << endl;
    cin >> name >> age;

    for (size_t idx = 0; idx < TOTAL_MARKS; ++idx)
    {
        cin >> mark;
        marks.push_back(mark);
    }

    if (!IsValidInput())
    {
        throw std::invalid_argument("wrong arguments");
    }
}

void Student::putdata()
{
    cout << name << " " << age << " " << std::accumulate(marks.begin(),marks.end(),0) << " " << cur_id << endl;
}

bool Student::IsValidInput()
{
    if (name.empty() || (name.length() > 100))
    {
        return false;
    }

    if ( age <= 0 || age > 80 )
    {
        return false;  
    }

    for (size_t idx = 0; idx < marks.size(); ++idx)
    {    
        if ( marks[idx] < 0 || marks[idx] > 100 )
        {
            return false;
        }
    }
    return true;
}

int main()
{
    int totalObj = 0, type = 0;
    cout<< "Enter total obj : ";
    cin >> totalObj;

    std::vector<std::unique_ptr<Person<int>>> person;

    for (int idx = 0; idx < totalObj; ++idx)
    {
        cout<< "Enter obj type (1 or 2) : ";
        cin >> type;

        switch(type)
        {
            case 1:
            {
                std::unique_ptr<Person<int>> temp(std::make_unique<Professor>());
                temp->getdata();
                person.push_back(std::move(temp));
            }
            break;

            case 2:
            {
                std::unique_ptr<Person<int>> temp(std::make_unique<Student>());
                temp->getdata();
                person.push_back(std::move(temp));
            }
            break;

            default:
            break;
        }
    }

    for (auto itr = person.begin(); itr != person.end(); ++itr)
    {
        (*itr)->putdata();
    }

    return 0;
}

问题出在 switch case 1 和 2 中。我只是使用 int 来实例化 class Person。当我编译这个时,我得到以下错误

      In function 'int main()':
181:80: error: no matching function for call to 'std::unique_ptr<Person<int> >::unique_ptr(std::_MakeUniq<Professor>::__single_object)'
181:80: note: candidates are:
In file included from /usr/include/c++/4.9/memory:81:0,
                 from 3:
/usr/include/c++/4.9/bits/unique_ptr.h:228:2: note: template<class _Up, class> std::unique_ptr<_Tp, _Dp>::unique_ptr(std::auto_ptr<_Up>&&)
  unique_ptr(auto_ptr<_Up>&& __u) noexcept;
  ^
/usr/include/c++/4.9/bits/unique_ptr.h:228:2: note:   template argument deduction/substitution failed:
181:80: note:   'std::_MakeUniq<Professor>::__single_object {aka std::unique_ptr<Professor, std::default_delete<Professor> >}' is not derived from 'std::auto_ptr<_Up>'
In file included from /usr/include/c++/4.9/memory:81:0,
                 from 3:

请建议如何将 Professor and Student 向上转换为 Person std::unique_ptr

最佳答案

你的问题是StudentProfessor没有扩展 Person<int> .事实上,Person<int>Professor无关, 和每个 Person<...>是完全不同的类,彼此没有关系。

如果你想在一个 vector 中保存所有的人类型,你将需要一个多态类,它提供一个关于所有 Person<...> 的接口(interface)。 .

您的问题的解决方案是创建一个抽象的人:

struct AbstractPerson {
    virtual void getdata() = 0;
    virtual void putdata() = 0;
    virtual ~AbstractPerson() = default;
};

然后,生成Person<T>从那个类继承:

template<typename T>
struct Person : AbstractPerson {
    // ...
};

然后,替换不相关的std::vector<Person<int>>据此:

std::vector<std::unique_ptr<AbstractPerson>> person;

// ...

person.emplace_back(std::make_unique<Student>()); // works!

这是您的工作代码的实时版本,稍微现代化了: Live on Coliru

关于c++ - 使用 std::unique_ptr 使用 CRTP 进行向上转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43083845/

相关文章:

c++ - 程序在 Ubuntu 14.04 和 GCC 中编译不正确

c++ - 在 MinGW 上使用 SFML

c++ - 在编译时在 static_assert() 中显示整数

c++ - 如何声明指向 C++11 std::array 的指针?

c++ - 为什么 std::thread 通过转发引用接受仿函数

c++ - [C++14]我正在使用 pair 但没有输出

c++ - 从特里树中删除所有

c++ - 用 std::forward_as_tuple 模拟 std::forward

c++ - 自动将类成员注册到 C++ 中的包含对象

c++ - 本土工作队列与英特尔 TBB