在这种情况下,为什么Sfinae对我来说不正确以及如何修复它
Why SFINAE works incorrect for me in this case and how to fix it?
我试图在 struct A
中留下一个函数 foo
(prints 0),如果其参数具有模板方法 isA<void>
和另一个函数(如果没有)。此代码(以下最小示例简化为最小示例)编译(使用GCC 6.1.0和Clang-3.9.0尝试使用显式--std=c++14
选项)并运行。
,但是它打印了1,但是,我敢肯定,它会打印0。我想知道我在哪里错了,但真正的问题是:如何使这项工作正确?
请仅C 14解决方案。
#include <type_traits>
#include <iostream>
#include <utility>
using std::enable_if;
using std::declval;
using std::true_type;
using std::false_type;
using std::cout;
template<int M>
struct ObjectX
{
template<typename C>
bool isA() { return false; }
};
struct XX : ObjectX<23456> {
int af;
};
template <typename ObjType> using has_dep = decltype(declval<ObjType>().template isA<void>());
template <typename, typename = void>
struct has_isa : public false_type {};
template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType> > : public true_type {};
template<typename ObjType>
struct A
{
template<typename T = void>
typename enable_if<has_isa<ObjType>::value, T>::type
foo() {
cout << "called foo #0" << "n";
}
template<typename T = void>
typename enable_if<!has_isa<ObjType>::value, T>::type
foo() {
cout << "called foo #1" << "n";
}
};
int
main()
{
A<XX> axx;
// XX().template isA<void>(); -- to check, that we can call it and it exists
axx.foo();
return 0;
}
此程序中有两个问题。
首先,has_dep<XX>
是bool
。当我们尝试has_dep<XX>
时,添加默认模板参数意味着这确实是has_dep<XX, void>
。但是专业是has_dep<XX, bool>
-这与我们实际查找的不符。bool
与void
不匹配。这就是为什么has_dep<XX>
是false_type
。解决此问题的方法是std::void_t
,我建议您阅读该Q/A,以了解其工作原因。在您的专业化中,您需要使用void_t<has_dep<ObjType>>
。
第二,这是不对的:
template<typename T = void>
typename enable_if<has_isa<ObjType>::value, T>::type
sfinae仅发生在替换的直接上下文中,类模板参数不在函数模板替代的直接上下文中。正确的模式是:
template <typename T = ObjType> // default to class template parameter
enable_if_t<has_isa<T>> // use the function template parameter to SFINAE
foo() { ... }
进行这两个修复程序,并且该程序按预期工作。
您的sfinae失败了,因为has_isa
选择了错误的专业化。
使用has_isa<T>
必须是默认实现或专业版本。
按照您的定义,您有一个默认参数可以void:
// default argument ---------v
template <typename, typename = void>
struct has_isa : public false_type {};
然后在表达式has_isa<T>
中,第二个参数必须无效。大致与编写has_isa<T, void>
。
问题是:
template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType>> : public true_type {};
// ^--- what's that type?
即使模板部分订购会考虑此"超载"更专业,但不会选择它。查看has_dep
的定义:
struct XX {
template<typename C> bool isA() { return false; }
};
template <typename ObjType>
using has_dep = decltype(declval<ObjType>().template isA<void>());
嘿,该类型has_dep<T>
是t.isA<void>()
的返回类型,即bool
!
因此,专业版本看起来像这样:
template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType>> : public true_type {};
// ^--- really, this is bool in our case
因此,为了使其工作,您必须致电has_isa<T, bool>
。由于这是不切实际的,因此您应该定义自己的专业化:
template <typename ObjType>
struct has_isa<ObjType, void_t<has_dep<ObjType>>> : public true_type {};
其中void_t
被定义为:
template<typename...>
using void_t = void; // beware for msvc
因此,has_isa<T>
将始终考虑专业化,因为我们将void
作为第二个模板参数,现在我们的专业化始终将void
作为第二个参数。
此外,正如Barry所述,您的功能未正确形成,因为Sfinae仅在立即出现。您应该这样写:
template<typename T = ObjType>
typename enable_if<has_isa<T>::value, void>::type
foo() { // ^--- sfinae happens with T
cout << "called foo #0" << "n";
}
如果您不希望公开模板参数,只需将功能私有:
template<typename ObjType>
struct A {
public:
void foo() {
foo_impl();
}
private:
template<typename T = ObjType>
typename enable_if<has_isa<T>::value, void>::type
foo_impl() {
cout << "called foo #0" << "n";
}
template<typename T = ObjType>
typename enable_if<!has_isa<T>::value, void>::type
foo_impl() {
cout << "called foo #1" << "n";
}
};
您的问题是您专业的类别:
您应该强迫has_dep
返回void
。
template <typename ObjType> using has_dep = decltype(static_cast<void>(declval<ObjType>().template isA<void>()));
所以在这里
template <typename ObjType>
struct has_isa<ObjType, has_dep<ObjType> > : public true_type {};
// It is really <bjType, void> you specialize.
- 使用2个键的cpp-stl::优先级队列排序不正确
- 正弦级数方程计算不正确
- 我试图制作一个程序,要求用户输入问题和答案,但程序循环不正确
- 密码登录程序将永远循环并显示不正确的结果
- 在C++中返回不正确的楼层函数值
- 形状对象的旋转和缩放不正确C++
- C++ 读取文件读取文件不正确
- 关于类的 Python 文档 - 对C++的引用不正确
- 特征 LLT 模块给出不正确的结果?
- glibcxx STL 在实现 std::valarray::sum() 时是否不正确?
- 如果语句逻辑不正确
- 来自逆 vp 矩阵和相机位置的光线方向不正确
- 不正确的操作数类型 MSVC
- 数组填充了不正确的值
- 我是否不正确地集中了这些字符数组?
- 为什么除非我使用 cout,否则我的值不正确?
- 计算幂级数的数学结果不正确
- 从堆栈中读取字符后,如何修复不正确的文件输出
- 在这种情况下,为什么Sfinae对我来说不正确以及如何修复它
- 如何修复OpenSSL不正确的数据传输