我似乎无法弄清楚我哪里出了问题。 请参阅https://ideone.com/WKsZSN
我正在尝试创建一个仅当其参数是某种为迭代器公开 typedef 的模板化类时才存在的函数。
在无条件的情况下,函数看起来像这样:
template<template <class, class> class C, class T, class A>
void DoSomething(C<T,A>& val)
{
T* pT;
cout << "did something!\n";
}
在这种情况下,类型推导对于这个片段来说效果很好:
vector<int> v{1,2,3,4,5};
DoSomething(v);
好的。所以现在我想输入 -deruce 我的参数并启用_if 容器类公开 typedef 迭代器。使用 Herb Sutter Gotw sfinae 模式,我创建了:
template<class T> struct supports_iteration
{
private:
typedef char yes[1];
typedef char no[2];
template <class C> static yes& foo(typename C::iterator*);
template <class C> static no& foo(...);
public:
static constexpr bool value = sizeof(foo<T>(0)) == sizeof(yes);
};
好的,所以使用这个,我现在可以检测迭代器是否暴露:
vector<int> v{1,2,3,4,5};
DoSomething(v);
cout << "vector<int> supports_iteration? " <<
boolalpha << supports_iteration<decltype(v)>::value << "!" << endl;
工作正常并输出:
did something!
vector<int> supports_iteration? true!
好的,现在我想像这样使用enable_if升级DoSomething():
template<template <class, class> class C, class T, class A>
void DoSomethingSmartly(
typename std::enable_if<
supports_iteration<
C<T,A>
>::value
>::type& val)
{
T* pT;
cout << "did something smartly!\n";
}
但这不起作用。我明白了
prog.cpp:在函数‘int main()’中: prog.cpp:44:22:错误:没有匹配的函数可用于调用“DoSomethingSmartly(std::vector&)” DoSomethingSmartly(v);// - 失败!! ^ prog.cpp:26:6: 注意:候选:模板类 C、类 T、类 A> void DoSomethingSmartly(typename std::enable_if >::value>::type&) 无效巧妙地做一些事情( ^~~~~~~~~~~~~~~~~~~ prog.cpp:26:6:注意:模板参数推导/替换失败: prog.cpp:44:22:注意:无法推导出模板参数“模板类 C” DoSomethingSmartly(v);// - 失败!!
我做错了什么?
在您的尝试中,
C
、T
、A
处于不可推导的上下文中(在traits<T>::type
、T
中处于不可推导的上下文中),您可以在返回类型上使用enable_if
:
template<template <class, class> class C, class T, class A>
typename std::enable_if<supports_iteration<C<T,A>>::value>::type
DoSomethingSmartly(C<T, A>& val)
{
// ...
}
@Jarod42 在他的评论中给出了正确的答案,但我会用外行人的话添加:
当考虑只是...
template<template <class, class> class C, class T, class A>
void DoSomethingSmartly(
typename std::enable_if<
supports_iteration<C<T,A>>::value>::type&);
...编译器无法从向量参数中推断出 C、T、A 的类型,因为
C<T,A>
中的 support_iteration<C<T,A>>::value
处于不可推导的上下文中。
这个答案更详细地解释了它。
以下更改将解决此问题:
template<template <class, class> class C, class T, class A>
void DoSomethingSmartly(
C<T,A>& c, //Now deducible...
typename std::enable_if<supports_iteration<C<T,A>>::value>::type* = 0)
{
T* pT;
cout << "did something smartly!\n";
}
现在第一个参数用于推导 C、T、A,第二个参数用于根据 SFINAE 判断函数是否可调用。使用
* = 0
是为了让你永远不必传入额外的参数。
我想通了。我真正想要的是这个(实际上我并不关心迭代,它对于暴露语法 T::size() 函数来说是一个不好的代理):
template<template <class, class> class C, class T, class A,
typename = decltype(
declval<C<T,A>>().size()
,void()
)
>
void DoSomethingReallySmartly(C<T,A>& val)
{
T* pT;
cout << "did something really smartly!\n";
}
...但我仍然想知道为什么类型推导在最初的尝试中失败了!!!