简体   繁体   English

C ++模板和子类?

[英]C++ Templates and Subclasses?

So, I'm learning C++, and I've run into something which I know how to do in Java, but not in C++ :). 所以,我正在学习C ++,而且我遇到了一些我知道如何用Java做的事情,而不是用C ++ :)。

I have a template for a container object, which is defined as such: 我有一个容器对象的模板,定义如下:

template <class T>
class Container {
    vector<T> contained;

    public:

    void add(T givenObject) {
        this->contained.push_back(givenObject);
    }

    T get(string givenIdentifier) throw (exception) {
        for (int i = 0; i < this->contained.size(); i++) {
            if (this->contained[i].getIdentifier() == givenIdentifier) {
                return this->contained[i];
            }
        }
        throw new exception("An error has occured which has caused the object you requested to not be found. Please report this bug.");
    }

    bool empty() {
        return this->contained.empty();
    }

    bool identifierExists(string givenIdentifier) {
        for (int i = 0; i < this->contained.size(); i++) {
            if (this->contained[i].getIdentifier() == givenIdentifier) {
                return true;
            }
        }
        return false;
    }
};

This actually works very well, with one small issue. 这实际上非常有效,只有一个小问题。 It comes down to two lines: the first is the template definition and the second is 它归结为两行:第一行是模板定义,第二行是

this->contained[i].getIdentifer()

In Java, when declaring a Generic (template) one can define a superclass/interface which all members of T must extend in order to not create an error. 在Java中,当声明Generic(模板)时,可以定义一个超类/接口,T的所有成员必须扩展它才能不创建错误。 However, I'm not sure of a way to do this in C++, and my concern is that coupling the implementation here to a getIdentifier method which might not be defined is bad design. 但是,我不确定在C ++中这样做的方法,我担心的是将这里的实现耦合到一个可能没有定义的getIdentifier方法是糟糕的设计。

Now, it's not a huge deal if that's the case, this is just a little challenge project to help me learn the language, but I like to try to do things right. 现在,如果是这样的话,这不是一个大问题,这只是一个帮助我学习语言的挑战项目,但我喜欢尝试做正确的事情。 Is there a way to do what I'm thinking? 有办法做我想的吗? I know you can do it with primitives, for instance: 我知道你可以用原语来做,例如:

template <int T>

is valid, but when I try to use a user defined class, I get a compiler error. 是有效的,但是当我尝试使用用户定义的类时,我收到编译器错误。 Any suggestions? 有什么建议么?

It's not possible for you to put artificial limitations on template type parameters. 您不可能对模板类型参数设置人为限制。 If the type given doesn't support the way you use it, you'll receive a compiler error. 如果给定的类型不支持您使用它的方式,您将收到编译器错误。 A feature called 'concepts,' which would essentially allow this, was going to be added to the next C++ standard, but it was delayed to the next-next standard due to time constraints. 一个名为“概念”的功能,基本上允许这个,将被添加到下一个C ++标准,但由于时间限制,它被推迟到下一个标准。 If T doesn't have a visible getIdentifier() function, the instantiation won't compile. 如果T没有可见的getIdentifier()函数,则实例化将不会编译。

Template parameters need to be deduced at compile time. 模板参数需要在编译时推导出来。 template<int T> is valid because the first template parameter is an integer; template<int T>有效,因为第一个模板参数是一个整数; you can instantiate it with any constant integer. 你可以使用任何常量整数来实例化它。 If you attempted to use it with a non-const integer variable, it wouldn't compile. 如果您尝试将其与非const整数变量一起使用,则无法编译。 An instance of a class isn't a compile time constant, so it can't be used. 类的实例不是编译时常量,因此不能使用它。

You've gotten a couple of other answers, both quite good (especially @dauphic's, IMO). 你已经得到了其他几个答案,两者都很好(特别是@dauphic's,IMO)。 I'd just add that the code you've given really looks an awful lot like quite an inefficient imitation of an std::map . 我只是补充一点,你给出的代码看起来非常像是对std::map的低效模仿。 Under most circumstances, std::map will probably work better. 在大多数情况下, std::map可能会更好。 If you look up its interface, it'll also show you a way to decouple your container from having to specify getIdentifier() directly -- instead of directly using something like getIdentifier() , it uses a comparison functor that defaults to std::less<T> , which will (in turn) use T::operator< -- but you can also specify an entirely different comparison functor if you prefer. 如果你查看它的界面,它还会向你展示一种方法来解决你的容器直接指定getIdentifier() - 而不是直接使用像getIdentifier()这样的东西,它使用默认为std::less<T>的比较函数std::less<T> ,它将(反过来)使用T::operator< - 但如果您愿意,也可以指定完全不同的比较函子。

I should also point out that while others have pointed out that you'll get a compile error if you use getIdentifier (or whatever) and attempt to instantiate over a class that doesn't supply it. 我还应该指出,虽然其他人已经指出如果你使用getIdentifier (或其他)你会得到一个编译错误,并尝试实例化一个不提供它的类。 I feel obliged to warn you, however, that the error message you get may be long, convoluted, and quite difficult to decipher. 但是,我觉得有必要警告你,你得到的错误信息可能很长,很复杂,而且很难破译。 This is especially likely if there's some other type that does have a getIdentifier member available. 这是特别容易,如果有这确实有一些其他类型getIdentifier可用的成员。 In this case the error message you get say something like "Unable to to convert from type A to type B ", where type A is whatever type you used to instantiate the container, and type B is whatever (often entirely unrelated) type that happens to have a getIdentifier member. 在这种情况下,您得到的错误消息说“无法从type A转换为type B ”,其中type A是您用于实例化容器的任何类型, type B是发生的任何type B (通常完全不相关)类型拥有一个getIdentifier成员。 What's happening is that the compiler sees that you've used getIdentifier , and sees that type B has that, so it tries to convert your type A object to a type B object, and finds that it can't, so that's what it tells you about in the error message. 发生的事情是编译器看到你已经使用了getIdentifier ,并且看到type B有那个,所以它试图将你的type A对象转换为type B对象,并发现它不能,所以就是它所告诉的你在错误信息中。

PS Yes, I know this is really more of a comment than an answer. PS是的,我知道这不仅仅是一个评论,而是一个答案。 I apologize for that, but it won't really fit in a comment. 我为此道歉,但它不会真正适合评论。

You don't have to do anything. 你不需要做任何事情。 If contained[i] doesn't have a getIdentifer() function, you will get a compile-time error (just as you would using an interface contrainst in Java, and just as you would outside of using templates). 如果contained[i]没有getIdentifer()函数,则会出现编译时错误(就像在Java中使用接口一样,就像在使用模板之外一样)。

To elaborate: If you were to write, 详细说明:如果你要写,

 int x = 10;
 long id = x.getIdentifer();

That would be considered a "bad design". 这将被视为“糟糕的设计”。 It would just be a mistake, which the compiler will catch. 这只是一个错误,编译器会抓住它。 That is exactly what will happen in you're example. 这正是你的例子中会发生的事情。

C++ draws a pretty stark line between compile-time polymorphism (ie Templates) and run-time polymporphism (ie inheritance). C ++在编译时多态(即模板)和运行时多态(即继承)之间绘制了一条相当明显的界限。 So what you want to do is not supported by the language. 因此语言不支持您想要做的事情。

One typical practice to do what you are doing is to provide, in addition to T, a type that can get an identifier of a given T. This decouples the two behaviors into two types and you can specify (in English) the interface that must be implemented. 执行您正在做的事情的一种典型做法是除了T之外还提供可以获取给定T的标识符的类型。这将两种行为分离为两种类型,您可以指定(英语)必须的接口实施。

template <class T, class StringIdForT>
class Container 
{
  ...
   bool identifierExists(string givenIdentifier) 
   {
        StringIdForT idGetter;
        for (int i = 0; i < this->contained.size(); i++) 
        {
            if (idGetter.getIdentifier(this->contained[i]) == givenIdentifier) 
            {
                return true;
            }
        }
        return false;
   }  
};

You have a similar problem, StringIdForT must still define a specified method 您有类似的问题,StringIdForT仍然必须定义指定的方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM