此代码有效:
// Code A
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
template <typename T>
struct S {
template <typename Iter, typename = typename enable_if<is_constructible<T, decltype(*(declval<Iter>()))>::value>::type>
S(Iter) { cout << "S(Iter)" << endl; }
S(int) { cout << "S(int)" << endl; }
};
int main()
{
vector<int> v;
S<int> s1(v.begin()); // stdout: S(Iter)
S<int> s2(1); // stdout: S(int)
}
但是下面的这段代码不起作用。在下面的代码中,我只想继承
std::enable_if
,因此,如果所选版本的is_iter_of
具有成员typedef type
,则类std::enable_if
将具有成员typedef type
。// Code B
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
template <typename Iter, typename Target>
struct is_iter_of : public enable_if<is_constructible<Target, decltype(*(declval<Iter>()))>::value> {}
template <typename T>
struct S {
template <typename Iter, typename = typename is_iter_of<Iter, T>::type>
S(Iter) { cout << "S(Iter)" << endl; }
S(int) { cout << "S(int)" << endl; }
};
int main()
{
vector<int> v;
S<int> s1(v.begin());
S<int> s2(1); // this is line 22, error
}
错误信息:
In instantiation of 'struct is_iter_of<int, int>':
12:30: required by substitution of 'template<class Iter, class> S<T>::S(Iter) [with Iter = int; <template-parameter-1-2> = <missing>]'
22:16: required from here
8:72: error: invalid type argument of unary '*' (have 'int')
错误消息令人困惑:当然,我希望模板替换失败。因此可以选择正确的构造函数。为什么SFINAE无法在
Code B
中工作?如果invalid type argument of unary '*' (have 'int')
冒犯了编译器,则编译器也应该为Code A
发出相同的错误。 最佳答案
问题是,您尝试从std::enable_if
扩展,但是如果可能,您放入enable中的表达式可能无效。由于您使用的是一个继承该形式的类,因此您实例化的类将从一个无效的表达式继承,从而导致错误。
为enable_if
表达式命名的一个简单解决方案是使用别名而不是类:
template <typename Iter, typename Target>
using is_iter_of = enable_if<is_constructible<Target, decltype(*(declval<Iter>()))>::value>;
SFINAE仍可以使用别名正常工作。
这是因为实例化实例是您尝试对其应用SFINAE的功能的一部分。对于继承,表达式是要实例化的类的一部分,而不是函数。这就是为什么您遇到硬错误的原因。
事实是,在您的情况下应用SFINAE的方法有多种。让我们看一下SFINAE可能发生的地方:
enable_if< // here -------v
is_constructible<Target, decltype(*(declval<Iter>()))>::value
>::type
// ^--- here
实际上,将发生SFINAE,因为如果bool参数为false,则
enable_if::type
将不存在,从而导致SFINAE。但是,如果仔细观察,可能不存在另一种类型:
decltype(*(std::declval<Iter>()))
。如果Iter
是int
,则询问star运算符的类型是没有意义的。因此,也可以在SFINAE上应用。如果您作为
Iter
发送的每个类都具有*
运算符,则可以使用继承解决方案。由于int
不存在,因此您要向std::is_constructible
发送不存在的类型,从而使构成基类的整个表达式无效。使用别名,使用
std::enable_if
的整个表达式将适用SFINAE。而基类方法仅将SFINAE应用于std::enable_if
的结果。关于具有decltype : substitution failure becomes an error?的C++ SFINAE,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45891716/