【问题标题】:Assigning a template value to a class template via a pointer to its non-template parent class通过指向其非模板父类的指针将模板值分配给类模板
【发布时间】:2013-02-28 01:14:08
【问题描述】:

我正在尝试创建一个可以存储模板值的 C++ Template 类。但是,我需要在知道模板值的类型之前创建指向此类的指针。为此,我创建了模板类继承自的抽象 Base 类。我创建了指向Base 的指针,当它们被分配时,我使用basePtr = new Template<TYPE>

这里的问题是给Template 赋值。我能想到的每一种方法(我想使用重载的赋值运算符)都需要一个带有模板数据类型的方法作为形式参数。因为Template对象只能通过Base指针访问,所以我要在Base中做一个虚函数。但是虚方法不能包含模板数据类型,Base的签名中的虚方法必须与Template的方法一致。

这是我想要的一个例子:

class Base {
    public:
        /* THIS IS ILLEGAL - can't have template virtual method
        template <class V>
        virtual void operator =(const V& newValue) = 0;
        */
};

template <class ValueType>
class Template : public Base {
    private:
        ValueType *value;
    public:
        void operator =(const ValueType& newValue) {
            *value = newValue;
        }
};

int main() {
    Base *theObject;                  // Datatype for template not known 
    theObject = new Template<string>; // At this point, it will be known

    // NOW, THIS DOESN'T WORK - no virtual assignment overload in Base
    *theObject = "Hello, world!";

    return 0;
}

如果我以完全错误的方式处理此问题,并且我的方法很愚蠢,我深表歉意——这是我第一次尝试真正的 OOD。有没有办法解决我没有看到的这个问题?我知道我可以在Base 中创建一长串纯虚函数,这些函数使用不同的输入类型重载赋值运算符,如下所示:

virtual void operator =(const string& newValue) = 0;
virtual void operator =(const int& newValue) = 0;
virtual void operator =(const long& newValue) = 0;
...

但是,我希望用户能够将自定义类对象(或者,更有可能是指向这些对象的指针)插入到 Template::value,而我无法使用上述方法来完成。

【问题讨论】:

  • 当不兼容类型的值分配给Template&lt;&gt; 类的对象时,您希望发生什么?例如。当我执行Base* pObj = new Template&lt;int&gt;(); *pObj = "Hello" 时会发生什么?
  • 理想情况下,delete pObj; pObj = new Template&lt;string&gt;; *pObj = "Hello";。当我需要开始为Base 指针重置模板数据类型时,我还没有达到这一点,所以我还不完全确定该怎么做。这是将数据存储在表中的类的一部分。 TableRows 组成,Rows 由Cells 组成,Template 是我的单元格对象。
  • 好的,但是这个任务*pObj = "Hello" 不能做所有这些事情。是不是抛出异常,让client代码可以做到?
  • 应该是,老实说我在学习中还没有接触过异常处理。如果我不能直接在单元格上使用赋值运算符,那也没关系。我可以在Row 中创建一个函数,该函数采用单元格的索引和要插入的新值,并将其插入cell[idx]
  • Row 将包含 Cell 指针 (Base **cell) 的动态数组,并根据需要使用 new 分配内存。

标签: c++ templates inheritance polymorphism virtual


【解决方案1】:

实现您想要实现的目标的一种方法是使用Boost.Any(一个著名的仅标头库)。下面是一个演示。代码是一步一步注释的,应该能看懂,here就是一个活生生的例子:

#include <stdexcept>       // Standard header for exception classes
#include <string>          // Standard header for std::string
#include <boost/any.hpp>   // The Boost.Any library header
#include <iostream>        // This program prints some output

class Base
{
public:
    virtual void operator = (boost::any val) = 0;
    //                       ^^^^^^^^^^
    //                       This can hold (almost) "any" value

    // You need a virtual destructor if you want to delete objects
    // of subclasses of this class through a pointer to this class!
    virtual ~Base() { }
};

template <class ValueType>
class Template : public Base
{
private:
    ValueType value;
//  ^^^^^^^^^
//  I do not see why using a pointer in this case. Manual memory
//  management just complicates things. However, if your really
//  need to do it and your really know what you're doing, go ahead.
//  Just remember to delete your pointer at destruction and not to
//  dereference it before it points to an allocated object (in your
//  original text, both of these things are NOT done correctly).
public:
    virtual void operator = (boost::any val)
    {
        // Attempt a cast to detect if the value we are trying to
        // assign to this object is of the appropriate type...
        ValueType* pVal = boost::any_cast<ValueType>(&val);
        if (pVal == nullptr)
        {
            // ...it is not! Throw an exception...
            throw std::logic_error("Incompatible type");
        }

        // The value is OK: assign it...
        value = *pVal;
    }
};

int main()
{
    Base *theObject;
    theObject = new Template<std::string>;

    try
    {
        // This assignment will succeed...
        // Wrapping the string literal in a std::string object is necessary
        // because boost::any cannot be initialized from an array (and in C++
        // string literals are arrays of characters).
        *theObject = std::string("Hello, world!");

        // This assignment will fail!
        *theObject = 1;
    }
    catch (std::logic_error const& e)
    {
        // Handle the exception...
        std::cout << e.what();
    }

    delete theObject; // <=== DON'T FORGET THIS!

    return 0;
}

【讨论】:

  • 哇,非常感谢。乍一看,这看起来正是我所需要的。我将不得不仔细查看 Boost.Any 并更仔细地检查您记录良好的代码,但我认为这可能会做到这一点。我希望 Template::value 成为一个指针,仅仅是因为我希望能够保存指向现有对象的指针,而不仅仅是对象数据的副本;这样,客户端程序可以创建数据,将指针放入表中,修改数据,这些修改将反映在表中。此外,他们可以使用表 [] 重载来访问/修改其数据。
  • 我可以创建一个单独的 Template 类来专门处理指针。否则,我需要一个bool isPointer; 数据成员,以便Template 类会记住它被传递了一个指针——如果是,我不想在Template 析构函数中执行delete value; 命令。
  • @RichStarbuck:好的,然后考虑使用智能指针(shared_ptrunique_ptr)。通常,您希望避免通过原始指针进行手动内存管理。无论如何,如果这将解决您的问题或至少回答您的问题并给您一个良好的起点,请考虑接受这个答案:-)
  • 我肯定会研究智能指针,我以前从未见过/听说过它们,但我一直在寻找新的技术来学习。我正试图弄清楚如何“接受”你的答案,但还没有看到......另外,在我获得 15 个声誉之前,我显然无法“+1”你的答案,这是我的第一篇文章。对不起:(
  • @RichStarbuck:没关系,即使您无法投票,您仍然可以接受答案。但你现在不需要这样做,只要确保它之前有帮助。
【解决方案2】:

听起来 boost::any 有点像您正在寻找的东西。不需要奇怪的继承层次结构,它可以自己存储任意类型(有一定的限制)。

现在我关心的是你打算如何重新获得你的价值?您的代码如何确定该单元格是否包含 int、std::string 或其他一些它以前从未听说过的用户定义类型,而不是尝试将其 any_cast 到某个类型列表(如果类型未列出...)?

【讨论】:

  • 是的,我现在正在研究 boost::any,看起来这正是我所需要的。想一想,收回价值将是一件棘手的事情。到目前为止,我所做的只是使用重载的 print(os);但是,获得价值本身会更加困难。我想一次一步,现在我更关心生成值表。
  • 我想我需要在模板中使用 returnValue() 方法。 Template 方法的返回值将是 ValueType (为其转换 Template 的模板数据类型),但我的理解是返回类型在重载方面不会影响签名,所以我应该能够制作一个Base 中的纯虚函数,可以通过 Base 指针访问。
  • 你可以创建一个虚函数......但是你打算在基类中声明它返回什么?因为我假设您将通过 Base* 变量调用它,编译器将强制执行 Base 声明它返回的任何类型。在您知道它是 ValueType 类型之前,您将无法使用它的 ValueType 版本。
猜你喜欢
  • 1970-01-01
  • 2020-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-04
相关资源
最近更新 更多