还有哪些其他语言支持“部分专业化”?

发布于 2024-07-11 07:13:58 字数 1235 浏览 12 评论 0原文

部分模板特化是 C++ 泛型编程最重要的概念之一。 例如:实现通用交换函数:

template <typename T>
void swap(T &x, T &y) {
  const T tmp = x;
  y = x;
  x = tmp;
}

将其专门化为向量以支持 O(1) 交换:

template <typename T, class Alloc>
void swap(vector<T, Alloc> &x, vector<T, Alloc> &y) { x.swap(y); }

因此,当您在通用函数中调用 swap(x, y) 时,始终可以获得最佳性能;

如果您可以用替代语言发布等效内容(或者如果该语言不支持交换概念,则该语言的部分专业化的规范示例),我们将不胜感激。

编辑:所以看起来很多回答/评论的人真的不知道什么是部分专业化,并且通用交换示例似乎妨碍了某些人的理解。 一个更一般的例子是:

template <typename T>
void foo(T x) { generic_foo(x); }

部分专业化是:

template <typename T>
void foo(vector<T> x) { partially_specialized_algo_for_vector(x); }

完全专业化是:

void foo(vector<bool> bitmap) { special_algo_for_bitmap(bitmap); }

为什么这很重要? 因为您可以在通用函数中调用 foo(anything):

template <typename T>
void bar(T x) {
  // stuff...
  foo(x);
  // more stuff...
}

并在编译时获得最合适的实现。 这是 C++ 以最小的性能损失实现抽象的一种方法。

希望它有助于理清“部分专业化”的概念。 在某种程度上,这就是 C++ 进行类型模式匹配的方式,而不需要显式模式匹配语法(例如 Ocaml/F# 中的 match 关键字),这有时会妨碍泛型编程。

Partial template specialization is one of the most important concepts for generic programming in C++. For example: to implement a generic swap function:

template <typename T>
void swap(T &x, T &y) {
  const T tmp = x;
  y = x;
  x = tmp;
}

To specialize it for a vector to support O(1) swap:

template <typename T, class Alloc>
void swap(vector<T, Alloc> &x, vector<T, Alloc> &y) { x.swap(y); }

So you can always get optimal performance when you call swap(x, y) in a generic function;

Much appreciated, if you can post the equivalent (or the canonical example of partial specialization of the language if the language doesn't support the swap concept) in alternative languages.

EDIT: so it looks like many people who answered/commented really don't known what partial specialization is, and that the generic swap example seems to get in the way of understanding by some people. A more general example would be:

template <typename T>
void foo(T x) { generic_foo(x); }

A partial specialization would be:

template <typename T>
void foo(vector<T> x) { partially_specialized_algo_for_vector(x); }

A complete specialization would be:

void foo(vector<bool> bitmap) { special_algo_for_bitmap(bitmap); }

Why this is important? because you can call foo(anything) in a generic function:

template <typename T>
void bar(T x) {
  // stuff...
  foo(x);
  // more stuff...
}

and get the most appropriate implementation at compile time. This is one way for C++ to achieve abstraction w/ minimal performance penalty.

Hope it helps clearing up the concept of "partial specialization". In a way, this is how C++ do type pattern matching without needing the explicit pattern matching syntax (say the match keyword in Ocaml/F#), which sometimes gets in the way for generic programming.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

ㄖ落Θ余辉 2024-07-18 07:13:58

D 支持部分专业化:

(扫描上述链接中的“部分”)。

特别是第二个链接将为您提供非常详细的细分,说明您可以使用模板专业化做什么,不仅在 D 中,而且在 C++ 中也是如此。

这是交换的 D 特定示例。 它应该打印出专门用于 Thing 类的交换消息。

import std.stdio;    // for writefln

// Class with swap method

class Thing(T)
{
public:

    this(T thing)
    {
        this.thing = thing;
    }

    // Implementation is the same as generic swap, but it will be called instead.
    void swap(Thing that)
    {
       const T tmp = this.thing;
       this.thing = that.thing;
       that.thing = tmp;
    }

public:

    T thing;
}


// Swap generic function

void swap(T)(ref T lhs, ref T rhs)
{
    writefln("Generic swap.");

    const T tmp = lhs;
    lhs = rhs;
    rhs = tmp;
}

void swap(T : Thing!(U))(ref T lhs, ref T rhs)
{
    writefln("Specialized swap method for Things.");

    lhs.swap(rhs);
}

// Test case

int main()
{
    auto v1 = new Thing!(int)(10);
    auto v2 = new Thing!(int)(20);

    assert (v1.thing == 10);
    assert (v2.thing == 20);
    swap(v1, v2);
    assert (v1.thing == 20);
    assert (v2.thing == 10);

    return 0;
}

D supports partial specialization:

(scan for "partial" in the above links).

The second link in particular will give you a very detailed breakdown of what you can do with template specialization, not only in D but in C++ as well.

Here's a D specific example of swap. It should print out the message for the swap specialized for the Thing class.

import std.stdio;    // for writefln

// Class with swap method

class Thing(T)
{
public:

    this(T thing)
    {
        this.thing = thing;
    }

    // Implementation is the same as generic swap, but it will be called instead.
    void swap(Thing that)
    {
       const T tmp = this.thing;
       this.thing = that.thing;
       that.thing = tmp;
    }

public:

    T thing;
}


// Swap generic function

void swap(T)(ref T lhs, ref T rhs)
{
    writefln("Generic swap.");

    const T tmp = lhs;
    lhs = rhs;
    rhs = tmp;
}

void swap(T : Thing!(U))(ref T lhs, ref T rhs)
{
    writefln("Specialized swap method for Things.");

    lhs.swap(rhs);
}

// Test case

int main()
{
    auto v1 = new Thing!(int)(10);
    auto v2 = new Thing!(int)(20);

    assert (v1.thing == 10);
    assert (v2.thing == 20);
    swap(v1, v2);
    assert (v1.thing == 20);
    assert (v2.thing == 10);

    return 0;
}
挽清梦 2024-07-18 07:13:58

恐怕 C# 不支持部分模板特化。

部分模板专业化意味着:

您有一个带有两个或多个模板(泛型/类型参数)的基类。
类型参数将是

在派生(专用)类中,您可以指示类型参数之一的类型。
类型参数可能类似于

因此,当有人使用最后一个类型参数为 int 的类(实例化其对象)时,将使用派生类。

I am afraid that C# does not support partial template specialization.

Partial template specialization means:

You have a base class with two or more templates (generics / type parameters).
The type parameters would be <T, S>

In a derived (specialized) class you indicate the type of one of the type parameters.
The type parameters could look like this <T, int>.

So when someone uses (instantiates an object of) the class where the last type parameter is an int, the derived class is used.

深爱不及久伴 2024-07-18 07:13:58

Haskell 有重叠实例作为扩展:

class Sizable a where
  size :: a -> Int

instance Collection c => Sizable c where
  size = length . toList

是一个查找任何集合大小的函数,它可以有更具体的实例:

instance Sizable (Seq a) where
  size = Seq.length

另请参阅 HaskellWiki 上的高级重叠

Haskell has overlapping instances as an extension:

class Sizable a where
  size :: a -> Int

instance Collection c => Sizable c where
  size = length . toList

is a function to find size of any collection, which can have more specific instances:

instance Sizable (Seq a) where
  size = Seq.length

See also Advanced Overlap on HaskellWiki.

情何以堪。 2024-07-18 07:13:58

实际上,您可以(不完全是;见下文)使用扩展方法在 C# 中执行此操作:

public Count (this IEnumerable<T> seq) {
    int n = 0;
    foreach (T t in seq)
        n++;
    return n;
}

public Count (this T[] arr) {
    return arr.Length;
}

然后调用 array.Count() 将使用专用版本。 “不完全是”是因为解析取决于数组的静态类型,而不是运行时类型。 即这将使用更通用的版本:

IEnumerable<int> array = SomethingThatReturnsAnArray();
return array.Count();

Actually, you can (not quite; see below) do it in C# with extension methods:

public Count (this IEnumerable<T> seq) {
    int n = 0;
    foreach (T t in seq)
        n++;
    return n;
}

public Count (this T[] arr) {
    return arr.Length;
}

Then calling array.Count() will use the specialised version. "Not quite" is because the resolution depends on the static type of array, not on the run-time type. I.e. this will use the more general version:

IEnumerable<int> array = SomethingThatReturnsAnArray();
return array.Count();
风蛊 2024-07-18 07:13:58

C#:

void Swap<T>(ref T a, ref T b) {   
  var c = a;   
  a = b;   
  b = c;
}

我猜(纯)Haskell 版本是:

swap :: a -> b -> (b,a)
swap a b = (b, a)

C#:

void Swap<T>(ref T a, ref T b) {   
  var c = a;   
  a = b;   
  b = c;
}

I guess the (pure) Haskell-version would be:

swap :: a -> b -> (b,a)
swap a b = (b, a)
栀梦 2024-07-18 07:13:58

Java 有泛型,它允许您做类似的事情。

Java has generics, which allow you to do similar sorts of things.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文