在阅读另外一个问题,我来到了一个问题,有部分订购的,这是我的削减以下试验的情况下

template<typename T>
struct Const { typedef void type; };

template<typename T>
void f(T, typename Const<T>::type*) { cout << "Const"; } // T1

template<typename T>
void f(T, void*) { cout << "void*"; } // T2

int main() {
  // GCC chokes on f(0, 0) (not being able to match against T1)
  void *p = 0;
  f(0, p);
}

对于这两种功能的模板,该功能类型的专业化,进入过载决议 void(int, void*).但是部分排序(根据科莫和海湾合作委员会),现在说这第二个模板的是更专业化。但为什么?

让我去通过的部分排序,并显示在这里我具有的问题。可 Q 是一个独特的造型用于确定部分订购的根据 14.5.5.2.

  • 变换参数的列表 T1 (Q插入): (Q, typename Const<Q>::type*).这类参数 AT = (Q, void*)
  • 变换参数的列表 T2 (Q插入): BT = (Q, void*), ,这也是种类型的论点。
  • 非转化参表对 T1: (T, typename Const<T>::type*)
  • 非转化参表对 T2: (T, void*)

由于用C++03下指定这一点,我有没有使用意向的,我读了关于在若干缺陷的报告。上述改变参数列表 T1 (称为 AT 我)用作参数列表 14.8.2.1 "推断模板参数的一个函数的电话".

14.8.2.1 不需要转变 ATBT 自己了(一样,除参考声明,等等),直到 14.8.2.4, ,独立于每 A / P 对不型扣除:

  • AT 反对 T2: { (Q, T), (void*, void*) }. T 是唯一的模板参数在这里,它会发现 T 必须 Q.类型的扣除成功对于平凡 AT 反对 T2.

  • BT 反对 T1: { (Q, T), (void*, typename Const<T>::type*) }.它会发现 TQ, 也在这里。 typename Const<T>::type* 是一个联合国推导出境,所以它不会被用于推断出任何东西。


这是我的第一个问题:这会现在使用的价值 T 推断出第一参数?如果答案是否定的,则第一个模板的是更专业化。这不可能的情况下,因为这两个海湾合作委员会和科莫说,第二个模板的是更专业的,我不相信他们是错误的。所以我们假定"是",并插入 void*T.第(14.8.2.4)说 "扣减是独立完成对每个对和结果,然后合并的" 并且还 "在某些情况下,然而,值没有参与类型的扣除,而是使用价值的模板的论据是推断在其他地方或者明确指定"。 这听起来像是"是的"。

扣除因此成功过,对于每一个P的一对。现在,每个模板的是,至少作为专门为其他的,因为扣除不是还依赖于任何隐性转换,并成功地在两个方向。结果,对话应该是模糊的。

因此,我的第二个问题:现在,为什么实现中说,第二个模板的是更专业的?什么时候有没有忽视我?


编辑:我测试了明确的专业化和实例,这两者,在最近的海湾合作委员会的版本(4.4)告诉我所提到的专业化是模棱两可,而旧版本的海湾合作委员会(4.1)不会升起这种模糊性错误。这表明,海湾合作委员会最近的版本有不一致的部分订购的用于功能的模板。

template<typename T>
struct Const { typedef void type; };

template<typename T>
void f(T, typename Const<T>::type*) { cout << "Const"; } // T1

template<typename T>
void f(T, void*) { cout << "void*"; } // T2

template<> void f(int, void*) { }
  // main.cpp:11: error: ambiguous template specialization 
  // 'f<>' for 'void f(int, void*)'
有帮助吗?

解决方案

这是我在这。我同意 查尔斯*贝利 这不正确的步骤是从 Const<Q>::Type*void*

template<typename T>
void f(T, typename Const<T>::type*) { cout << "Const"; } // T1

template<typename T>
void f(T, void*) { cout << "void*"; } // T2

该步骤,我们想要的是:

14.5.5.2/2

鉴于两个超载功能的模板,一个人是否是更多的专门比另一个可以确定通过将每个模板中转和使用的参数扣减(14.8.2)进行比较。

14.5.5.2/3-b1

对每种类型的模板参数,合成一个独特的类型和替代品,对于每次出现的这一参数的功能清单参数,或者用一个模板转换功能,在返回的类型。

在我看来,这些类型的合成如下:

(Q, Const<Q>::Type*)    // Q1
(Q, void*)              // Q2

我没有看到任何的措辞,要求的第二个合成参数的 T1void*.我不知道有任何先例,在其他情况下。的类型 Const<Q>::Type* 是完全有效的类型在C++类型的系统。

所以现在我们执行该扣减步骤:

Q2T1

我们尽量推断模板参数T1因此我们有:

  • 参1: T 推导为 Q
  • 参数2:Nondeduced上下文

即使参数2是一个非上下文推断出,扣除仍然有成功,因为我们有一个值T.

Q1T2

推断模板参数T2我们有:

  • 参1: T 推导为 Q
  • 参数2: void* 不匹配 Const<Q>::Type* 因此扣除故障。

恕我直言,这是标准可以让我们失望。的参数不依赖所以它不是真的清楚会发生什么情况,然而,我的经验(基于一个眯起眼睛读的14.8.2.1/3),即使在那里参数类型P不是依赖的,那么参数类型的一个应该与之相匹配。

合成参数的T1可以用来专门T2,而不是相反。T2因此更多的专门比T1和使是最好的功能。


更新1:

只是为了复盖果有关 Const<Q>::type 是无效的。考虑下面的例子:

template<typename T>
struct Const;

template<typename T>
void f(T, typename Const<T>::type*) // T1
{ typedef typename T::TYPE1 TYPE; }

template<typename T>
void f(T, void*)                    // T2
{ typedef typename T::TYPE2 TYPE ; }

template<>
struct Const <int>
{
  typedef void type;
};

template<>
struct Const <long>
{
  typedef long type;
};

void bar ()
{
  void * p = 0;
  f (0, p);
}

在上面, Const<int>::type 使用当我们执行通常载决议中的规则,而不是当我们得到的部分载的规则。它不会是正确的选择任意一个专业化 Const<Q>::type.它可能不是直观的,但编译器是很高兴有一个synthasized类型的形式 Const<Q>::type* 并利用它在类型扣除。


更新2

template <typename T, int I>
class Const
{
public:
  typedef typename Const<T, I-1>::type type;
};

template <typename T>
class Const <T, 0>
{
public:
  typedef void type;
};

template<typename T, int I>
void f(T (&)[I], typename Const<T, I>::type*)     // T1
{ typedef typename T::TYPE1 TYPE; }

template<typename T, int I>
void f(T (&)[I], void*)                           // T2
{ typedef typename T::TYPE2 TYPE ; }


void bar ()
{
  int array[10];
  void * p = 0;
  f (array, p);
}

Const 模板的实例有一定的价值 I, 它递归的实例化本身直到 I 达到0.这是当部分专用化 Const<T,0> 被选中。如果我们有一个编译器,它综合了一些真正的类型参数的函数,那么什么值将编译器选择的阵列的指数?说10吗?那么,这将是对上述实例,但它不会比赛的分专业化 Const<T, 10 + 1> 其中,在概念上至少,将导致一个无限的数字递归的实例。不管值得它所选择的我们可以修改的结尾条件是,值+1,然后我们就会有无限循环中的该部分排序的算法。

我看不出如何部分订购的算法可以正确地实例 Const 找到什么 type 真的是。

其他提示

编辑:在研究 铛的 执行(通过道格雷戈尔)的部分排序的算法,我已经同意与其他的海报,原来的例子不是'意在'是模糊的--尽管标准不清楚,因为它可能是约什么应该发生在这样的情况。我编辑这个职位来表明我修改过的想法(为自己的利益和参考).特别是铛的算法澄清说,'typename Const<T>::type'没有被翻译为'空隙期间的部分排序步骤和各A/P对推断相互独立的。

最初我不知道为什么以下被认为是模糊的:

        template<class T> void f(T,T*);  // 1

        template<class T> void f(T, int*); // 2

        f(0, (int*)0); // ambiguous

(The above is ambiguous because one cannot deduce f1(U1,U1*) from f2(T,int*), and going the other way, one cannot deduce f2(U2,int*) from f1(T,T*). Neither is more specialized.)

但是下面将不会模糊的:

        template<class T> struct X { typedef int type; };
        template<class T> void f(T, typename X<T>::type*); // 3
        template<class T> void f(T, int*); // 2

(的的理由之一可以期望它是不明确的是如果下面分别发生:
- f3(U1,X<U1>::type*) -> f3(U1, int*) ==> f2(T,int*) (deduction ok, T=U1)
- f2(U2,int*) ==> f3(T, X<T>::type*) (deduction ok, T=U2 makes X<U2>::type* -> int*)
如果这是真的,没有一个将更多的专门比其他。)

在研究铛的部分排序的算法是清楚的,他们享'3'上,如果这是:

template<class T, class S> void f(T, S*); // 4

因此扣除的一些独特的'U'反对'typename X::type'将会成功-

  • f3(U1,X<U1>::type*) is treated as f3(U1, U2*) ==> f2(T,int*) (deduction not ok)
  • f2(U2,int*) ==> f3(T,S* [[X<T>::type*]]) (deduction ok, T=U2, S=int)

等等'2'显然是更多的专门于'3'.

变换参表T1(Q 插入):(Q,typename Const::type*).的类型 辩论是在=(Q,void*)

我不知道如果这真的是一个正确的简化。当你揉合的类型 Q, ,你可以想像一个专业化 Const 为确定订的模板specliazation?

template <>
struct Const<Q> { typedef int type; }

这将意味着 T2 是不是至少作为专门为 T1 因为一个 void* 参数不匹配 T1第二个参数对于任何给定的模板参数。

编辑:请不顾此后在研究一连串的叮当声算法的部分排序,作为实施的过道格格雷(即使只是部分地实现作为这个写作-这似乎逻辑,是有关运的问题得到充分执行足够的)-看来,如果它把undeduced上下文仅仅是另一种模板参数。这表明,所载的明确void*参数,应该被更多的专门版本,并应有任何模糊性。作为通常的科莫是正确的。现在对于作为措辞的标准,清楚地定义这种行为就是另一回事...

由于这一员额还被张贴在comp.郎。c++。主持,并似乎引起一些混乱,那里了-我想我后我回答,小组也在这里-因为讨论是很明显有关的问题在这里。

On Jul 25, 1:11 pm, Bart van Ingen Schenau <b...@ingen.ddns.info> wrote:

You are going one step too fast here. How do you know (and would the compiler know) that there is no specialisation of Const<Q> such that Const<Q>::type != void?

As far as I can see, the compiler would transform the parameter-list of A to: AT=(Q, <unknown>*). To call B with these parameters requires an implicit conversion (<unknown>* to void*) and therefore A is less specialised than B.

我相信这是不正确的。当检查,看看它的功能是更多 专门(在部分排序)中,改变编译器 参表以 (Q, void*) -即它实际上相关的实例 模板(最好的匹配)和看起来它里面的价值"类型"-在这种情况下,基于 在主要的模板,这将是void*。

关于你的观点有关的部分专业化-在检查 哪个模板的是更多的专门比其他的唯一类型,可以用 是的独特产生的类型-如果还有其他专业点 的化的宣言(当载决议正在完成的) 他们将予以考虑。如果你加入他们后来,他们应该得到的选择 你将违反ODR(根据14.7.4.1)

部分/明确的专业也将获得在considertaion 形成的候选人设置的,但这段时间使用该种类型的实际参数 的功能。如果最好的匹配的部分专业化(X)结果 功能型的,具有更好的隐性转换序列对于一些 参数,那么我们永远不会让它的部分订购阶段, "更好的"功能将得到所选择的(前使它的部分 订购阶段)

这里是一个例子与关于什么的意见应该在各个步骤:

    template<class T, bool=true> struct X;  // Primary

    template<class T> struct X<T,true> { typedef T type; };  // A
    template<> struct X<int*,true> { typedef void* type; };  // B


    template<class T> void f(T,typename X<T>::type); //1
    template<class T> void f(T*,void*); //2


    int main()
    {
      void* pv;
      int* pi;


      f(pi,pi);   
      // two candidate functions: f1<int*>(int*,void*),  f2<int>(int*,void*)
      // Note: specialization 'B' used to arrive at void* in f1
      // neither has a better ICS than the other, so lets partially order
      // transformed f1 is f1<U1>(U1,X<U1,true>::type) --> f1<U1>(U1,U1) 
      //       (template 'A' used to get the second U1)
      // obviously deduction will fail (U1,U1) -> (T*,void*)
      // and also fails the other way (U2*, void*) -> (T,X<T>::type)
      // can not partially order them - so ambiguity 




      f(pv,pv);  
      // two candidate functions: f1<void*>(void*,void*), f2<void>(void*,void*)
      // Note: specialization 'A' used to arrive at second void* in f1
      // neither has a better ICS than the other, so lets partially order
      // transformed f1 is f1<U1>(U1,X<U1>::type) --> f1<U1>(U1,U1) 
      //       (template 'A' used to get the second U1)
      // obviously deduction will fail (U1,U1) -> (T*,void*)
      // and also fails the other way (U2*, void*) -> (T,X<T>::type)
      // can not partially order them - so ambiguity again             

    }

这也值得一提的是,如果主要的模板,没有一个定义-然后SFINAE工作期间的部分订购阶段中, 既可以从中推导出其他的,和含糊不清,应该结果。

如果你还添加另一个模板,这将导致另一场比赛中如果点的实例的任的那些职能移到其他地方的翻译单位,你将明显违反ODR.

On Jul 25, 1:11 pm, Bart van Ingen Schenau <b...@ingen.ddns.info> wrote:

第一,是更多的专业意味着这些 类型 该模板可以选择通过载决议。使用这个规则的部分排序可以概括为:尝试 找到一个类型一例,可以被称为但是B不是,或超载 决议更喜欢称A.如果该类型,可以发现,然后B是更多 专业,比A

不论点在这里。但是,根据该规则,因为它们目前,运的例子是 模糊不清。


最后,在这里都明确、毫不含糊的回答两个具体问题提出的litb:

1)将这个现在使用的T值的推导出的第一个参数?
是的-当然,它已,它是在做模板的论点减扣-- 的'链接'必须维持。

2)现在,为什么实现中说,二是更专业的而不是?
因为他们是错误的;)

我希望这将使这个问题的休息-请让我知道如果有任何仍是不清楚:)

编辑:litb提出了一个很好的点,在他的评论,也许说明的主要模板总是会得到 使用的实例,与独特的中产生的类型是太强大了发言。
还有一些情况下,主要的模板将不会被称为。
我得到的是当部分的排序发生的,一些产生的独特的类型 用来匹配的最好的专业化。你说得对,这没有成为主要的模板。我已经编辑以上的语言来这样做。他还提出了一个问题有关的定义一个更好的匹配的模板之后点的实例.这将是一个违反ODR根据节点的实例。


该标准说,一旦付对创建(使用的规则转变为描述临时。func。令)他们推导的针对对方使用模板参数扣减(temp.扣除)和部分处理的情况下,非推断的情况下,例化的模板和其嵌套的类型,触发点的实例.Temp.点部分处理的ODR侵犯行为(意义的部分排序不应改变不论点的实例之内的翻译单位)。我还是不确定的混淆是从未来? 费萨尔瓦利1小时前[删除这条评论]

litb:"注意的步骤,把Q入Const::类型建立的论点是不明确涵盖的SFINAE规则。该SFINAE规则的工作与参数扣除,把该段放Q进入功能的模板功能的参数清单在14.5.5.2.'

该SFINAE规则必须使用在这里-他们怎么能够不够?我觉得这是充分的暗示-我不会否认,它可以更加清晰,同时我鼓励委员会澄清 这个-我不认为它需要加以澄清,以解释你的例子充分。

让我提供一种方式链接。从(14.8.2):"当一个明确的模板参数列表是指定的,该模板的论据必须兼容 模板参数清单和结果必须在一个有效功能的类型如下所述;否则类型的扣除 失败"

从(14.5.5.2/3) "转变用是:—对每一种类型的模板参数,合成一个独特的类型和替代,每个发生的 该参数在功能的参数清单,或者对于转换功能的模板,在返回的类型。"

在我看来,上述报价,意味着一旦你"创建的"独特产生的类型为每个模板参数,功能宣言》已经被 implicity实例通过 明确 提供的独特的类型作为模板的论据我们的功能的模板。如果这个结果无效 功能的类型,然后不仅在转变,但更重要的是随后的模板参数扣除必要的 部分为功能失败。

从(14.5.5.2/4) "使用的转换功能的参数清单,执行参数扣减对其他功能的模板。转化的模板 至少是专门为其他 如果,只有如果, 中扣除的成功和推导出参数的类型 是一个完全一致(这样的扣除不依赖于隐性转换)."

如果转换功能的参数列表导致替换失败,那么我们知道扣除可能不具有成功。并且由于扣除没有成功,它不是作为专门为其他的-这就是我们所需要知道继续 在部分订购的两个。

litb:我也不知道什么会发生这样的情况: template<typename T> struct A; template<typename T> void f(T, typename A<T>::type); template<typename T> void f(T*, typename A<T>::type); 当然, 这是indended是有效的代码,但这样做A::类型,它将会失败,因为在 模板的定义方面,没有定义" 还注意到,没有POI定义为模板的实例所导致这个 一种替代,而试图确定一个排序(部分排序并不取决于 在任何环境。这是一个静态的酒店的两个功能的模板参与).我认为这看起来像一个问题在标准需要加以固定。

确定-我想我看到那里我们看到不同的事情。如果我理解正确的话,你是在说 作为这些功能的模板得到宣布,编译器是保持一个跟踪的部分排序在他们之中, 无论过载决议没有得到触发以选择他们之间。如果这就是你如何解释它,然后我可以看到为什么你会希望上述行为描述。但我不认为这个标准没有要求或授权。

现在,该标准是明确的,部分排序是不可知的类型,是使用的要求的功能(我相信 这是什么你指的是当你把它描述为一个静态的财产和这方面独立)。

标准也很清楚,它只关心部分排序(调用部分排序)之间的功能的模板 在该过程期间过载决议(13.3.3/1)如果并且只有如果它不能选择的更好的功能基于ICS或 如果一个人是一模板,并将其它不是。[部分订购的类模板的部分专业化是一个单独的问题 和在我的心使用的相关方面(其他模板的定义)要求的实例这一特定类。]

因此,在我看来,由于机械的部分排序功能的模板时调用超载 决议被执行,它使用一个相关的部分上下文(模板的定义和专业)可用 在这点在载的决议是正在做。

所以根据我的interepretation,根据你的榜样,使用模板结构的一个'上,代码是有效的。部分的排序不是在定义上下文。但如果你发生援引过载决议 两者之间的功能,通过编写一个叫f((int*)0,0)和在当时的编译器么 尝试组建一个候选人的声明或声明的部分了他们的(如果它得到部分订购的步骤) 如果无效的表达或类型的结果为部分的功能型的,SFINAE可以帮助我们,告诉 我们的模板,扣发生故障(尽部分排序而言,这意味着一个 不能将更多的专门比其他的如果我们甚至不能改变的模板)。

现在,作为方面的兴趣点-如果你相信,作为我该转换功能的类型应该 代表隐含的实例,使用明确提供的模板参数清单(使用的唯一生成的类型) 那么下面的标准报价是相关的:

14.6.4.1/1个功能专业化的模板,一个成员的功能的模板的专业化,或专门用于一个 部件的功能或静态数据的成员一类的模板,如果专业化是隐含地实例 因为它是引用从另一个模板的专业化和背景下从中引用它 取决于一个模板参数,指的实例的专门化的实例 封闭的专业化。

我的解释是,该兴的转换功能的类型和origianl功能类型的 同POI为这些职能创造的实际功能的电话。

litb:由于部分排序而不是仅仅 a property of the syntactic form of parameters (i.e "T*" against "T(*)[N]"), 我会投票修订的规范(如"如果Q中出现的一套名称符的 一个合格-id命名一种类型,那么类型命名为"Q") 或者说类型名为是另一个独特的类型。 This means that in template<typename T> void f(T, typename Const<T>::type*); the argument list is (Q, R*), for example. Same for template<typename T> void f(T*, typename ConstI<sizeof(T)>::type); the arg lisst would be (Q*, R). A similar rule would be needed for non-type parameters, of course. 我会必须想一想,并提出一些试验案例看,如果这将产生的自然顺序,虽然。

啊-现在就你提出一个可能的解决办法可以解决的模糊性,在有利于我们 所有直观地希望--这是一个单独的问题,而我喜欢的方向,你的标题, 像你,我也会必须把一些想法进入它之前宣布其加工性。

谢谢你继续讨论。我希望如此不只是限制你把评论意见。

因为你可以编辑我的职位,请随时内作出回应的后果是更容易。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top