使用enable_if和SFINAE时,函数参数类型推导(std容器,例如向量)失败

时间:2017-04-21 07:12:04

标签: c++ sfinae enable-if type-deduction template-templates

我似乎无法弄清楚我哪里出错了。 见https://ideone.com/WKsZSN

我正在尝试创建一个函数,只有当它的参数是某种模板类暴露一个itdef for iterator时才存在。

在非条件情况下,函数看起来像这样:

public String scene = new String();

public  int number; 

private void jButtonAPerformed(java.awt.event.ActionEvent evt){
    scene = "A";
    number+=1; 
}

private void jButtonBPerformed(java.awt.event.ActionEvent evt){
    scene = "B";
    number+=1; 
}

Location l = new Locaation(scene, number);

在这种情况下,类型推导适用于此代码段:

template<template <class, class> class C, class T, class A>
void DoSomething(C<T,A>& val)
{
    T* pT;
    cout << "did something!\n";
}

确定。所以现在我想输入-deduce我的参数和enable_if容器类暴露typedef迭代器。使用草药sutter得到了sfinae模式,我创造了:

vector<int> v{1,2,3,4,5};
DoSomething(v);

好的,所以使用这个,我现在可以检测是否暴露了迭代器:

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;

好的,现在我想使用enable_if升级DoSomething(),如下所示:

did something!
vector<int> supports_iteration? true!

但这不起作用。我得到了

prog.cpp:在函数'int main()'中: prog.cpp:44:22:错误:没有匹配函数来调用'DoSomethingSmartly(std :: vector&amp;)'   DoSomethingSmartly(v); // - 失败!                       ^ prog.cpp:26:6:注意:候选人:模板类C,类T,A类&gt; void DoSomethingSmartly(typename std :: enable_if&gt; :: value&gt; :: type&amp;)  void DoSomethingSmartly(       ^ ~~~~~~~~~~~~~~~~~ prog.cpp:26:6:注意:模板参数扣除/替换失败: prog.cpp:44:22:注意:无法推导模板参数'模板类C'   DoSomethingSmartly(v); // - 失败!!

我做错了什么?

3 个答案:

答案 0 :(得分:2)

在您的尝试中,CTA处于不可导出的上下文中(在traits<T>::typeT in in non deducible context),您可以在返回类型上使用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)
{
   // ...
}

答案 1 :(得分:1)

@ 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处于不可推导的上下文中。

This answer更详细地解释了它。

以下更改可解决此问题:

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以便您永远不必传递附加参数。

答案 2 :(得分: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";
}

...但我仍然想知道为什么类型演绎在原始尝试中失败了!!!