为什么这些模板不明确?

时间:2010-12-14 01:37:23

标签: c++ templates

本书C++ Templates : The Complete Guide在第275页有一个例子,我无法绕过头。

引用书中摘录......

template <typename T>
class Promotion<T,T> {
  public:
    typdef T ResultT;
};

template<typename T1, typename T2>
class Promotion<Array<T1>, Array<T2> > {
  public:
    typedef Array<typename Promotion<T1,T2>::ResultT> ResultT;
};

template<typename T>
class Promotion<Array<T>, Array<T> > {
  public:
    typedef Array<typename Promotion<T,T>::ResultT> ResultT;
};
  

不幸的是,部分特化Promotion<Array<T1>, Array<T2> >是   既不比局部专业化更专业也更不专业化   Promotion<T,T>。为避免模板选择歧义,最后部分   专业化增加了。它比以前任何一个都更专业   两个部分专业化。

为什么前两个模板不明确,为什么最后一个模板解决了模糊问题?当我尝试应用规则时,我或者无法弄清楚它是如何产生歧义的,或者如果我认为我有办法让它发生,我不知道为什么最后一个模板解决了这个问题。

5 个答案:

答案 0 :(得分:15)

也许你的困惑源于这种关系“更专业而非”的关系。它是部分订单,而不是总订单 - 这意味着给定2个模板特化,并不总是一个比另一个更专业。

Anon的评论是对的:假设第3个专业化不存在,后来你的代码中有:

Promotion<Array<double>, Array<double> > foo;

(当然你可能实际上不会创建这个空结构类型的变量,但这只是强制实例化的最简单方法。)

鉴于此foo的声明,将挑选前两个专业中的哪一个?

  • 专业化1适用于T = Array<double>
  • 专业化2适用于T1 = doubleT2 = double

这两个专业都适用,所以我们需要确定哪个“比另一个更专业”,并选择那个。怎么样?我们会说XY更专业,如果它至少与Y一样专业,但Y至少不是X专门为X。虽然看起来这只是围绕这个问题跳舞,但我们可以用一个聪明的规则来回答这个新问题:

Y至少与X一样专业,如果无论我们为Y的模板参数分配什么类型,结果类型都可以始终与{匹配{1}}。

请注意,我们忘记了当前实例化中涉及的特定类型(在这种情况下,double) - “至少是专门的”关系是部分特化本身的属性,并且没有不依赖于特定的实例化。

专业化1能否始终与专业化2相匹配?这个过程有点像代数。我们要求任何类型T,我们可以找到类型T1T2,以便:

Promotion<Array<T1>, Array<T2> > = Promotion<T, T>

这意味着:

Array<T1> = T
Array<T2> = T

所以答案是否定的。仅查看第一个隐含结果,给定任何类型T,通常无法找到类型T1,因此Array<T1>T的类型相同。 (如果T恰好是Array<long>,则会有效,但如果Tintchar*或大多数其他类型,则无效。)

另一种方式呢?专业化2总能与专业化1相匹配吗?我们要求任何类型T1T2,我们可以找到类型T,以便:

Promotion<T, T> = Promotion<Array<T1>, Array<T2> >

意味着:

T = Array<T1>
T = Array<T2>

所以答案是否定的。给定任何类型T1,始终可以找到类型T,使TArray<T1>的类型相同 - 只需字面设置T = Array<T1>。但一般情况下,其他类型T2不限于与T1相同,如果不是(例如T1 = boolT2 = float)则不可能找到与TArray<T1>相同的类型Array<T2>。所以一般来说,找不到这样的类型T

在这种情况下,不仅专业化比其他专业化更专业化,甚至也不像那样专门化。因此,如果需要实例化此模板类并且两个特化项都匹配 - 就像Anon给出的示例中那样 - 没有办法选择“最佳”模板类。

答案 1 :(得分:3)

这是因为在推导参数时,第一个参数的一个模板更好,而第二个参数更好。

让我们看一下Promotion< Array<S>, Array<S> >

两位候选人都可以匹配。在第一个参数上,Promotion< T, T >通过推断T = Array<S>进行匹配。通过推断Promotion< T1, T2 >来匹配T1 = S。第二个是更好的匹配,因为Array<T1>T更具体。

在第二个参数上,Promotion< T = Array<S>, T = Array<S> >是完全匹配。通过推断Promotion< T1 = S, T2 >来匹配T2 = S。由于Array<S>Array<T2>更匹配,因此第一个更具体。

选择最佳模板的规则表明,与所有其他候选人相比,一个参数必须是更好的匹配,而所有其他参数都不会更差。由于没有候选人符合这些标准,因此不明确。

第三个专业化符合标准(第一个参数与Array<T1>一样好,第二个是完美的),因此它解决了歧义。

如果您真的想让自己头脑旋转,请尝试阅读标准中的[temp.deduct.partial]部分,其中所有这些规则都在良好的律师中说明。在n3225草案中它是14.8.2.4,其他版本的编号可能会有所不同。

答案 2 :(得分:3)

j_random_hackeranswered个问题,但作为一个具体的例子,只是直接使用标准规则:

template< class T >
class Array {};

template< class T1, class T2 >
class Promotion {};

template <typename T>                   // a
class Promotion<T,T> {
public:
    typedef T ResultT;
};

template<typename T1, typename T2>      // b
class Promotion<Array<T1>, Array<T2> > {
public:
    typedef Array<typename Promotion<T1,T2>::ResultT> ResultT;
};

// template<typename T>
// class Promotion<Array<T>, Array<T> > {
//   public:
//     typedef Array<typename Promotion<T,T>::ResultT> ResultT;
// };


//---------------------------- §14.5.4.2/1:

template< class T >
void a_( Promotion< T, T > );                      // a

template< class T1, class T2 >
void b_( Promotion< Array< T1 >, Array< T2 > > );  // b


//---------------------------- §14.5.5.2/3:

class aT {};
class bT1 {};
class bT2 {};

void a( Promotion< aT, aT > );                          // a
void b( Promotion< Array< bT1 >, Array< bT2 > > );      // b

void test()
{
    // Check if the concrete 'a' arguments fit also 'b':
    b_( Promotion< aT, aT >() );
    // Fails, so a is not at least as specialized as b

    // Check if the concrete 'b' arguments fit also 'a':
    a_( Promotion< Array< bT1 >, Array< bT2 > >() );
    // Fails, so b is not at least as specialized as a
}    

免责声明:我不得不再次重新自学。

我发布这个是因为有人Else™发现我的例子具有启发性,因此,也可能对读者有所帮助。

干杯&amp;第h。,

答案 3 :(得分:2)

歧义是指同一个类可以从不同的特化实例化,并且编译器不能支持一个特化而不是另一个特化,因为它们都不是更专业的。添加更专业的版本会使编译器选择该版本。这解决了歧义,因为C ++规则规定应该通过不太专业化的模板选择最专业的模板。

答案 4 :(得分:2)

查看专业化在概念层面上的作用。

第一个说“如果T1和T2相同,这就是我们应该做的事情。”

第二个说“如果T1和T2都是数组模板,这就是我们应该做的事情”。

好的,所以......让我们说T1和T2都是两个相同的阵列模板。我们应该使用哪一个?没有什么好的方法可以证明其中一个比另一个更适合这种情况。

我们通过添加专门针对该情况的第三个模板专门化来解决这个问题:“如果T1和T2都是相同的阵列模板,那么这就是我们应该做的事情。”