1

假设我有一个类模板,其中一些方法是特定于类型的。

template <typename T>
class Shape
{
...
void Foo();
...
};

现在我将专门使用特定类型的函数,例如:

void Shape<bool>::Foo() {...};
void Shape<double>::Foo() {...};

问题:

  1. 如果 Foo 应该根据类型有不同的参数怎么办?是否可以使用与类声明中不同的参数为特定类型专门化方法?我怀疑这是不允许的,那么应该怎么做呢?

  2. 如何防止未定义 Foo 的类型的类实例化?

  3. 与我所描述的情况相比,什么时候应该使用虚函数和纯虚函数?是否可以混合模板和继承?

4

2 回答 2

1
  • 1个。如果 Foo 应该根据类型有不同的参数怎么办?是否可以使用与类声明中不同的参数为特定类型专门化方法?

不。是的,正如下面的 Nathan Monteleone 所指出的,这可以通过完全专门化 Shape 类来实现。

  • 1b。我怀疑这是不允许的,那么应该怎么做呢?

你能把函数本身变成一个独立于类的模板吗

class Shape {
    //...
    template <typename T>
    void Foo();
    //...
};
  • 2. 如何防止未定义 Foo 的类型的类实例化?

在类体内:

template<typename U> //note the different typename
class Shape {
    static_assert(std::is_same<U, bool>::value || std::is_same<U, double>::value, "Type forbidden");
    //...
    inline void Foo() {
        if( std::is_same<U, bool>::value ) Foo_impl_bool();
        else if( std::is_same<U, double>::value ) Foo_impl_double();// and so on.
        // this function is likely to get optimized since the conditionals are constexpr
    }
    private:
    void Foo_impl_bool();//...
};

3a。与我所描述的情况相比,什么时候应该使用虚函数和纯虚函数?

1b 和 2 的答案是 2 个单独的解决方案,如果它们都不可能,则其他可能性是:

  1. 将该函数提取到它自己的类中。

  2. 单独为此函数使用接口继承。

3b。是否可以混合模板和继承?

是的

于 2013-04-16T20:02:04.910 回答
0

(1) 有点,但不完全是您尝试的方式。您基本上希望专业化来定义功能;这与声明基类和派生类时有很大不同。

template <typename T> class Shape {
    static_assert(false, "Not implemented");
};

template <> class Shape<bool> {
    void Foo(bool a, std::string b) {...}
};

template <> class Shape<int> {
    void Foo(int, std::string, whatever else) {...}
};

即使您要在原始的非特化 Shape 中定义 Foo,它也不会影响特化。完全专业化不会扩展原始模板,而是替换它!

(2) 使用静态断言。请参见上面的示例。

(3) 是的,您可以混合使用模板和继承。虚函数运行时多态和静态多态。模板参数必须在编译时知道,而运行时多态对象的确切类型则不需要,因为它们可以被基类引用引用。在上面的模板示例中,您不能简单地说

template <class T> Shape {...};

...

Shape* myShape;   // Compiler error: Shape requires a template parameter

但是,通过继承,您可以:

class ShapeBase { virtual void Foo() = 0; };
class Circle : public ShapeBase { virtual void Foo() { ... } };
class Square : public ShapeBase { virtual void Foo() { ... } };
...

Shape* myShape = new Circle;
myShape->Foo(); // calls Circle->Foo()

请注意,当您使用继承时,函数签名必须匹配!这就是您将决定推迟到运行时所付出的代价。

于 2013-04-16T20:50:23.463 回答