std::enable_if的不明确的部分专门化
ambiguous partial specializations with std::enable_if
我在如下条件下遇到了一个问题:
#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
然后叮当++-3.1工作正常。
是编译器错误还是标准错误?
在这个问题中也出现了类似的问题
由于第二个和第三个部分专用化都与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
参数中,但是,编译器在参数推导过程中不会为您这样做。
UPDATE:响应Johannes Schaub-litb:您是对的,将std::check<int>
放入std::enable_if
的代码不会在Ideone和MSVC++2010上编译。该怎么办?根据14.8.2.4第11条【临时扣除部分】
在大多数情况下,所有模板参数都必须具有值,以便推导成功,但出于偏序目的,模板如果参数未在用于部分排序的类型。[注:模板参数在非推导上下文中使用被认为是使用的。——尾注][示例:
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更英勇。
您有
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
- Visual C++(VS2017)中用户定义的转换不明确
- 重载类方法的不明确调用
- 为函数定义符号不明确的指针参数
- 父类的私有函数会导致对具有相同名称和相似参数的子类中的公共函数的不明确调用
- 在 C++17 中的命名空间和子命名空间中重载运算符是不明确的
- C++ 编译器错误:P1LinkedList.cpp:145:错误:重载的"to_string(int&)"调用不明确
- 对重载函数find_first_not_of的不明确调用
- 不明确的成员模板查找
- gcc出现不明确的模板实例化错误
- 调用'Node'构造函数是不明确的
- 如何解决不明确的运算符过载问题?
- 使用 nullptr 调用重载方法是不明确的
- "fpclassify":对重载函数的不明确调用
- 对"列表"的引用不明确,包括头文件
- 删除全局隐式函数 - 避免使用不明确的运算符
- 当接收到不明确的规范时,表示图的邻接列表的数据结构
- "byte":使用Crypto++和Windows SDK时出现不明确的符号错误
- 使用 std::iterator_traits<> 时编译时错误不明确
- 为什么这种明确的模板专门化不起作用
- std::enable_if的不明确的部分专门化