此代码有效:
// 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 {{1},则类type
将具有成员typedef std::enable_if
}。
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
}
错误信息令人困惑:当然我希望模板替换失败..因此可以选择正确的构造函数。为什么SFINAE不在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')
中工作?如果Code B
冒犯了编译器,编译器也应该为invalid type argument of unary '*' (have 'int')
发出相同的错误。
答案 0 :(得分:5)
问题是表达式*int
(*(declval<Iter>())
)无效,因此您的模板失败。你需要另一个模板级别,所以我建议采用void_t方法:
通过一个源自is_iter_of
或true_type
fals_type
在类的定义中使用enable_if
来启用迭代器构造函数。
要理解的关键是你的构造函数在需要typename is_iter_of<Iter, T>::type
的类型之外,enable_if
中的struct is_iter_of
导致整个事情的格式不正确。由于没有后备模板,因此编译错误。
template<class...>
using voider = void;
template <typename Iter, typename Target, typename = void>
struct is_iter_of : std::false_type{};
template <typename Iter, typename Target>
struct is_iter_of<Iter, Target, voider<decltype(*(declval<Iter>()))>> : std::is_constructible<Target, decltype(*(declval<Iter>()))> {};
template <typename T>
struct S {
template <typename Iter, typename std::enable_if<is_iter_of<Iter, T>::value, int>::type = 0>
S(Iter) { cout << "S(Iter)" << endl; }
S(int) { cout << "S(int)" << endl; }
};
如果voider
是格式错误的表达式(*(declval<Iter>())
),则附加*int
会使模板专精化不受欢迎,因此选择了后备基本模板(std::false_type
)
否则,它将从std::is_constructible``. In other words, it can still derive from
std :: false_type if the expression is well-formed but it's not constructibe, and
true_type`派生而来。
答案 1 :(得分:4)
问题是你试图从std::enable_if
扩展,但你在启用中的表达式可能无效。由于您使用的是继承表单的类,因此您实例化的类将从无效表达式继承,因此会出错。
为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
,则询问星号运算符的类型是没有意义的。所以SFINAE也适用于那里。
如果您以Iter
发送的每个班级都有*
运算符,那么您的继承解决方案就会有效。由于int
不存在,您将向std::is_constructible
发送一个非现有类型,使整个表达式构成基类无效。
使用别名,使用std::enable_if
的整个表达式适用于SFINAE。而基类方法仅对std::enable_if
的结果应用SFINAE。