使用enable_if和SFINAE时,函数参数类型推导(标准容器,例如向量)失败[重复]

问题描述 投票:0回答:3

我似乎无法弄清楚我哪里出了问题。 请参阅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++ sfinae enable-if type-deduction template-templates
3个回答
2
投票

在您的尝试中,

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)
{
   // ...
}

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

这个答案更详细地解释了它。

以下更改将解决此问题:

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
是为了让你永远不必传入额外的参数。


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";
}

...但我仍然想知道为什么类型推导在最初的尝试中失败了!!!

© www.soinside.com 2019 - 2024. All rights reserved.