此代码有效:
// 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)
}
Run Code Online (Sandbox Code Playgroud)
但是下面的代码不起作用.在下面的代码中,我只想继承std::enable_if,因此如果所选版本具有成员typedef ,则该类is_iter_of将具有成员typedef .typestd::enable_iftype
// 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
}
Run Code Online (Sandbox Code Playgroud)
错误信息:
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')
Run Code Online (Sandbox Code Playgroud)
错误信息令人困惑:当然我希望模板替换失败..因此可以选择正确的构造函数.为什么SFINAE没有工作Code B?如果invalid type argument of unary '*' (have 'int')冒犯了编译器,编译器也应该发出相同的错误Code A.
问题在于表达式*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; }
};
Run Code Online (Sandbox Code Playgroud)
voider如果*(declval<Iter>())是格式不正确的表达式(*int),则附加选项使模板专业化不是首选,因此选择了后备基本模板(std::false_type)。
否则,它将从std::is_constructible``. In other words, it can still derive fromstd :: false_type if the expression is well-formed but it's not constructibe, andtrue_type 派生。
问题是您试图从 扩展std::enable_if,但您在启用 if 中放入的表达式可能无效。由于您使用的类继承自该类,因此您实例化的类继承自无效表达式,因此会出现错误。
为表达式命名的一个简单解决方案enable_if是使用别名而不是类:
template <typename Iter, typename Target>
using is_iter_of = enable_if<is_constructible<Target, decltype(*(declval<Iter>()))>::value>;
Run Code Online (Sandbox Code Playgroud)
使用别名时,SFINAE 仍将按预期工作。
这是因为实例化别名是您尝试应用 SFINAE 的函数的一部分。通过继承,表达式是被实例化的类的一部分,而不是函数的一部分。这就是你遇到严重错误的原因。
问题是,SFINAE 有多种方式应用于您的案例。让我们看看 SFINAE 可能发生在什么地方:
enable_if< // here -------v
is_constructible<Target, decltype(*(declval<Iter>()))>::value
>::type
// ^--- here
Run Code Online (Sandbox Code Playgroud)
确实,SFINAE 会发生,因为enable_if::type如果 bool 参数为 false,则不会存在,从而导致 SFINAE。
但如果你仔细观察,可能不存在另一种类型:decltype(*(std::declval<Iter>()))。如果Iter是int,则询问星型运算符的类型是没有意义的。所以 SFINAE 也适用于那里。
Iter如果您发送的每个类都具有可用的运算符,那么您的继承解决方案将会起作用*。由于 withint它不存在,因此您将不存在的类型发送到std::is_constructible,从而使形成基类的整个表达式无效。
有了别名,整个使用表达式std::enable_if都以SFINAE为准。而基类方法只会将 SFINAE 应用于 的结果std::enable_if。
| 归档时间: |
|
| 查看次数: |
579 次 |
| 最近记录: |