模板类型推导失败(std :: empty作为谓词)

时间:2018-05-28 12:50:55

标签: c++ c++17

我有一个向量的向量,我想检查它们是否都是空的。使用标准库,我尝试了:

#include <algorithm>
#include <vector>

int main()
{
   std::vector<std::vector<int>> vv;

   std::all_of(std::begin(vv), std::end(vv), std::empty);
}

这导致clang 7.0:

中出现以下错误
  

的/ usr / bin中/../ LIB / GCC / x86_64的-Linux的GNU / 6.3.0 /../../../../包括/ C ++ / 6.3.0 /比特/ stl_algo.h :508:5:   注意:候选模板被忽略:无法推断模板参数   &#39; _Predicate&#39;

由于类型演绎的规则,我认为这是一种标准行为。但无论如何,我最简单的解决方法是什么?

编辑:我接受了rubenvb的回答,因为他给出了一个简单而合理的解释,以及自然的解决方法。 all_of接受谓词,谓词是函数,函数对象或lambda表达式。 std :: empty既不是那些,也不是函数模板。当显式实例化它时,我们得到一个应该工作的普通函数。令人惊讶的是,它仍然没有在我尝试的大多数编译器上编译。

好吧,让我们看看:

在GCC 6.3上,编译得很好 - https://godbolt.org/g/Pxta7C

但是在来自主干的GCC上,它会导致内部编译器错误 - https://godbolt.org/g/H6DHt5

来自主干或2017年MSVC的Clang都没有成功编译它:

https://godbolt.org/g/819pbQ(Clang)

https://godbolt.org/g/ua5E8e(MSVC)

EDIT2:显然,Robert Andrzejuk也是对的:编译器无法处理它的原因是模糊的重载决策。 std :: empty有3个不同的重载。其中两个同样很好的候选者:一般的一个和std :: initializer列表一个。我使用以下最小版本获得了类似的结果:

#include <vector>

template<class T>
void foo(const T& t);

template<class T>
void foo(const std::initializer_list<T>& il);

template<class F>
void bar(F f);


int main()
{
   bar(foo<std::vector<int>>);
}

但有一点不同。这个例子根本不能在GCC中从trunk编译(而不是导致ICE)。

5 个答案:

答案 0 :(得分:2)

不幸的是,区分重载的模板函数存在问题,因为SmtpClient GenMailClient = new SmtpClient(); GenMailClient.Send(message); 也是模板函数。更好的解释:std::function fails to distinguish overloaded functions

为此,需要std::all_of到正确的函数类型:static_cast

bool ( * )( const std::vector< int >& )

使用有关所需std::all_of( vv.begin(), vv.end(), static_cast< bool ( * )( const std::vector< int >& ) >( std::empty ) ); 的知识,我们可以创建一个帮助模板函数,从容器类型中推导出正确的定义:

辅助函数:

static_cast

它的用法示例:

template< typename C >
inline auto overloaded_pred_for( const C&, bool ( *f )( const C& ) ) -> decltype( f )
{
    return f;
}

答案 1 :(得分:1)

快速解决方法

#include <algorithm>
#include <vector>

int main() {
   std::vector<std::vector<int>> vv;

   std::all_of(std::begin(vv), std::end(vv), 
    [](const auto &v) {return std::empty(v);});
}

答案 2 :(得分:1)

  

我最简单的解决方法是什么?

也许

std::all_of(std::begin(vv), std::end(vv),
            [](auto const & v){ return v.empty(); });

答案 3 :(得分:1)

std::empty is a function template,因此不是一个可调用的对象。通过显式提供模板参数,可以将函数模板名称转换为可调用的具体实例:

#include <algorithm>
#include <iterator>
#include <vector>

int main()
{
   std::vector<std::vector<int>> vv;

   std::all_of(std::begin(vv), std::end(vv), std::empty<std::vector<int>>);
}

Live demo (which incidentally crashes the compiler)。另请注意,此GCC版本显然需要#include <iterator>,即使明确提到std::empty也应该使用例如#include <vector>ctrl + shift + r ...

答案 4 :(得分:-1)

问题是没有std :: empty这样的东西,据我所知,它是一个成员函数。尝试使用[](const auto& i){ return i.empty(); } 代替。

编辑: 好吧,我之前没有见过std :: empty但是正如下面的评论者指出它存在,但你仍然可以使用lambda