【问题标题】:Partial template specialization: matching on properties of specialized template parameter部分模板特化:匹配特化模板参数的属性
【发布时间】:2010-05-29 06:13:04
【问题描述】:
template <typename X, typename Y> class A {
    // Use Y::Q, a useful property, not used for specialization.
};
enum Property {P1,P2};
template <Property P> class B {};
class C {};

有没有办法定义A 的部分特化,使A&lt;C, B&lt;P1&gt; &gt; 成为A 的普通模板,但A&lt;C, B&lt;P2&gt; &gt; 是特化?

针对 Marcelo 进行编辑:更具体地说,不仅应该选择 B,还应该选择任何表现出特定属性的类型,例如它是第一个参数是 P2 的模板.

目标是使用Y 为A 提供一个漂亮的界面,允许编写类似A&lt;C, Y&lt;P2,Q&gt; &gt; 的内容。


用模板模板参数替换Y 模板参数会很好,但是有没有办法基于P 部分专门化它呢?

本意是这样写:

template <typename X, template <Property P> typename Y> class A {};
template <typename X> class A<X,template<> Y<P2> > {}; // <-- not valid

编辑以响应 In silico:我说将 Y 设为模板模板参数会很好,但实际上这违背了我想要做的目的,即使用Y 将逻辑链接的属性组合在一起,但仍基于这些子属性之一专门化 A。


有没有办法通过将特征添加到专业化 template &lt;&gt; class B&lt;P2&gt; 然后在 A 中使用 SFINAE?目的是写如下内容:

template <> class B<P2> {
    typedef int IAmP2;
};

// The following is not valid because it's a simple redefinition.
template <typename X, typename Y> class A {
    // Substitution using this template would fail for Y<P1>, and only the 
    // general template would be left for selection.
    typename Y::IAmP2 skipIfNotP2;
};

【问题讨论】:

  • 你想具体做什么?可能有可能实现你想要的,但我不太明白你的问题。 X 和 Y 在您的预期应用程序中代表什么?
  • 顺便说一下,SFINAE 仅适用于函数重载,不适用于模板特化。
  • 假设您有 Matrix,并且您希望能够执行类似 Matrix> 或 Matrix> 之类的操作,但是需要针对所有密集存储方案的专用矩阵版本。
  • 我明白了,我不知道 SFINAE 仅适用于函数重载,谢谢。
  • 这不是真的。 SFINAE 适用于任何演绎上下文,也涵盖部分模板特化。

标签: c++ sfinae partial-specialization template-templates


【解决方案1】:

我不明白你的意思。模板模板参数似乎是解决方案,尽管您以某种方式说它们不起作用。为什么不这样做?

template <typename X, typename Y> 
class A {
};

template <typename X, template<typename> class Y, typename P> 
class A< X, Y<P> > {
  /* property is P */
};

对于您的 SFINAE 问题,是的,这也是可能的

template <typename X, typename Y, typename Sfinae = void> 
class A {
};

template <typename X, typename Y> 
class A< X, Y, typename Y::IAmP2 > {
  /* Y is the class having a property */
};

class Sample {
  typedef void IAmP2;
};

我仍然不确定你的意思。

【讨论】:

  • 非常感谢约翰内斯。您的两个提案(模板模板和 SFINAE)都是正确的。这正是我一直在寻找的机制,只是我不知道语法。对于模板模板解决方案,扩展模板参数列表是我监督的(读过一次但不记得了)。但最后我将使用 SFINAE 解决方案,它更灵活,因为它不需要我声明固定数量的模板模板参数。再次感谢一堆,今天学到了一些:)。
【解决方案2】:

如果没有模板模板参数(我不知道在这种情况下使用它),它应该相当简单:

template <> class A<C, B<P2> > { ... };

其实太简单了。我一定错过了什么,但我看不到什么。

【讨论】:

  • 感谢马塞洛的回答。目的是让这个模板特化不仅适用于 B,而且适用于任何类型 Y。实际上,希望 P2 成为满足这种特化的类型的区分属性只是使它们不同的一种方式。我在这方面很灵活。关键是要弄清楚是否有一种方法可以在实例化时与类型的更一般的元属性/特征匹配,而不仅仅是它们的名称。
【解决方案3】:

这是你想要的吗? (使用 Visual Studio 2005 测试)

enum Property { P1, P2 }; 

template <Property P> class B {}; 
class C {};

// Other similar types, for the purpose of testing
template <Property P> class AnotherB {};
class AnotherC {};

// Primary template
template <typename X, template<Property P> class Y, Property P> class A
{
public:
    A() { ::printf("Primary template\n"); }
};

// Partial specialization for P2
template <typename X, template<Property P> class Y> class A<X, Y, P2>
{
public:
    A() { ::printf("Partially specialized template\n"); }
};

int main()
{
    // Trying out some combinations
    A<C, B, P1> q;               // prints "Primary template"
    A<C, B, P2> w;               // prints "Partially specialized template"
    A<AnotherC, B, P1> e;        // prints "Primary template"
    A<AnotherC, B, P2> r;        // prints "Partially specialized template"
    A<C, AnotherB, P1> t;        // prints "Primary template"
    A<C, AnotherB, P2> y;        // prints "Partially specialized template"
    A<AnotherC, AnotherB, P1> u; // prints "Primary template"
    A<AnotherC, AnotherB, P2> i; // prints "Partially specialized template"
}

您尝试偏特化会导致编译器错误,因为您只能通过 templates 到模板模板参数。您不能为模板模板参数传入template &lt;&gt; class B&lt;P2&gt;,因为它是一个完整的类型,而不是模板。

对于main() 函数中的前两行代码,C 是一个完整的类型,我们将传递给A 的type 参数X。 B 是我们传递给A 的template 参数Y 的模板,该模板必须接受Property 作为唯一的模板参数。我们将Property 值(P1 或P2)分别传递给A 的nontype 参数P。当我们为模板A 的最后一个参数传入P2 时,编译器将看到特化并使用它——否则,编译器将使用主模板A。接下来的 6 行采用类似的模式。

【讨论】:

  • 感谢您的回答在 silico 中,它很有见地,但仍然不是我想要的(我的错)。我在问题中说将 Y 设为模板模板参数会很好,但实际上并非如此。我编辑了问题以使其更清楚。
  • 我实际上可能会使用这个模板模板模式来实现我想要的,但它需要将典型的调用从 A> 更改为 A,P2>,这 1) 违背了良好接口的目的 2) 需要一种机制来检查 B 的第一个参数是否与 A 的第三个参数相同。再次感谢,干杯。
  • 同样,这不起作用,因为B&lt;P2, Q&gt; 是一个完整的类型,您不能将其传递给模板模板参数。只需将B 传递给A 的模板模板参数,并分别提供P2 和Q。所以你会有类似A&lt;C, B, P2, Q&gt; 的东西。这样,您就不会重复 P2,同时解决问题 (1) 和 (2)。除非我真的不知道你想要什么。 :-)
  • 确实我再次说过模板模板解决方案,但随后给出了一个不使用模板模板的示例(A > 将是模板的实例化 class A {}; ,不使用模板模板)。不好意思,回复你的评论太仓促了。我在主要问题的 cmets 中添加了一个示例情况。感谢您的宝贵时间。
【解决方案4】:

我将通过Matrix 示例提供另一个答案来回应您的评论。

对于您的 Matrix 示例,您可以这样做:

enum MatrixOrder { ColumnMajor, RowMajor };

template<MatrixOrder Order> class Dense {};
template<MatrixOrder Order> class Sparse {};

template<typename T, template<MatrixOrder> class Storage, MatrixOrder Order>
class Matrix
{
public:
    Matrix() { ::printf("Primary\n"); }
};

template<typename T, MatrixOrder Order>
class Matrix<T, Dense, Order>
{
public:
    Matrix() { ::printf("Specialized\n"); }
};

int main()
{
    // Trying out some combinations...
    Matrix<double, Dense, ColumnMajor> a;  // Prints "Specialized"
    Matrix<double, Dense, RowMajor> b;     // Prints "Specialized"
    Matrix<double, Sparse, ColumnMajor> c; // Prints "Primary"
    Matrix<double, Sparse, RowMajor> d;    // Prints "Primary"
    Matrix<float, Dense, ColumnMajor> e;   // Prints "Specialized"
    Matrix<float, Dense, RowMajor> f;      // Prints "Specialized"
    Matrix<float, Sparse, ColumnMajor> g;  // Prints "Primary"
    Matrix<float, Sparse, RowMajor> h;     // Prints "Primary"
    return 0;
};

它与我上一个答案的模式相似。现在,所有采用Dense 的存储方案都将专门化。希望这会有所帮助,至少有一点帮助。 :-)

【讨论】:

  • 它工作得很好,但如果无法提供更好的界面,它是一种后备解决方案,因为它将所有参数扁平化到同一级别,没有逻辑分组。想要的接口可以做:Matrix,640,480>。使用建议的接口,1)我必须做 Matrix,RowMajor,640,480> 甚至更长 2)我也可以做 Matrix ,RowMajor,640,480> 在线性索引上存在语义不匹配问题。
  • 这些问题会随着参数的增多而放大。我想知道是否可以对模板参数进行这种分组的原因是:Matrix,Optimization>。当然,默认方式只是将所有这些都展平为 Matrix 但从它混合所有内容的意义上说它不是很好,我无法预先配置存储方案独立地,它可以作为模板参数传递。我猜这是一个极端的例子。
  • 啊,我想我知道这是怎么回事了。您似乎需要某种策略模式(请参阅en.wikipedia.org/wiki/Policy-based_design)。 Storage 和 Optimization 可以是通过向 Matrix 提供“部分”功能来决定 Matrix 行为的策略类。
  • 没听说过。这让我想起了很多混入,但它具有可能的静态分辨率并且没有开销,这听起来好得令人难以置信。看起来真的是好东西。这可能正是我一直在寻找的东西,非常感谢你真棒。
  • 不用担心;很高兴能够为您指明正确的方向。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-19
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
  • 1970-01-01
  • 2012-01-14
相关资源
最近更新 更多