std :: enable_if的模糊部分特化

时间:2012-07-16 07:30:14

标签: c++ templates partial-specialization disambiguation

我在下面的条件下遇到了问题:

#include <iostream>
#include <type_traits>

#define TRACE void operator()() const { std::cerr << "@" << __LINE__ << std::endl; }

template <class T>
struct check : std::true_type {};

template <class F, class T, class Check=void>
struct convert {
  TRACE;// first case
};

template <class F, class T>
struct convert<F*, T, typename std::enable_if<(check<F>::value && check<T>::value), void>::type> {
  TRACE; // second case
};

template <class T>
struct convert<int*, T, typename std::enable_if<(check<T>::value), void>::type> {
  TRACE; // third case
};

然后

convert<int*, int> c;
c();

将在g ++ - 4.5,g ++ - 4.6,g ++ - 4.7和clang ++ - 3.1(所有选项-std = c ++ 0x)中报告模糊的类模板实例化

但如果我将第三种情况的支票替换为

typename std::enable_if<(check<int>::value && check<T>::value), void>:type

然后clang ++ - 3.1工作正常。

是编译器错误还是标准错误?

2 个答案:

答案 0 :(得分:0)

你有

template <class F, class T>
struct convert<F*, T, typename std::enable_if<(check<F>::value && check<T>::value), void>::type> {
  TRACE; // second case
};

template <class T>
struct convert<int*, T, typename std::enable_if<(check<T>::value), void>::type> {
  TRACE; // third case
};

当你使用convert<int*, int> c;时,编译器无法选择他需要使用哪个结构,因为它们都适合。

请注意,您在第一个模板中使用check<F>::value。这意味着,即使您通过了int *,也会check<int>::value,而不是check<int *>::value

答案 1 :(得分:0)

question

出现了类似的问题

因为第二和第三部分特化都与convert<int*, int>匹配,编译器将构建两个测试函数模板,其中两个部分专用的类模板作为参数提供:

template <class F, class T> 
void fun2(convert<F*, T, typename std::enable_if<
    (check<F>::value && check<T>::value), void>::type>
);

template <class T> 
void fun3(convert<int*, T, typename std::enable_if<
    (check<T>::value), void>::type>
);
然后,编译器通过将一个函数的一组转换参数交叉替换到另一个函数来检查一个函数模板是否比另一个更专业,并检查是否可以推导出所有模板参数。如果这两种方式都有效,那么这两种功能都不会比另一种功能更加专业化,并且随之产生歧义。

这里的问题是std::enable_if< (check<F>::value && check<T>::value), void>::type>是一个非推断的上下文,在这个参数推导游戏中不会被评估。编译器仅检查通用表达式是否具有相同的结构形式(推导出::分隔符前面的任何内容),而不是它们是否具有相同的值(在这种情况下为true_type)。

只有在第三个部分专业化中添加额外的check<int>::value,第三个专业化才会变得比第二个专业化更专业化。另一个“修复”是手动将true_type放入Check参数中,但是,在参数推断期间,编译器不会为您执行此操作。

更新:回应Johannes Schaub - litb:你是对的,放入std::check<int> std::enable_if的代码无法在Ideone和MSVC ++上编译2010.该怎么办?根据 14.8.2.4第11条[temp.deduct.partial]

  

在大多数情况下,所有模板参数都必须具有值   扣除成功,但为了部分订购目的的模板   参数可以保持没有值,只要它没有被使用   用于部分排序的类型。 [注意:模板参数   在非推断的上下文中使用的被认为是使用的。 - 结束说明]   [实施例:

template <class T> T f(int); // #1
template <class T, class U> T f(U); // #2
void g() {
    f<int>(1); // calls #1
}

对于OP的代码,我将其解释为未使用的参数将是std::enable_if表达式。我的猜测是Clang 3.1会做一些与Ideone和MSVC ++不相符的表达式。我不明白,如果在上述引用的上下文中,标准是否需要:只应忽略未使用的模板参数,还是忽略未使用的模板表达式?标准中还有其他部分出现了诸如“不要求实现使用英雄效应”之类的短语。在这方面,也许Clang比MSVC ++或Ideone更具英雄气概。