繁体   English   中英

C++:模板function以function为参数,function以模板类型为参数

[英]C++: template function taking function which takes another function which takes reference to template type as parameter

考虑以下代码

struct S
{
    unsigned u;
    float f;

    S(unsigned u0, float f0) : u(u0), f(f0) {}
};

template<typename T> bool contains(std::vector<T> vec, T elem, bool equivalent(const T&, const T&))
{
    // some implementation
}

int main() {
    std::vector<S> vec{{15, 17.8}};

    std::cout << contains(vec, S(15,159.48), [](S& a, S& b) -> bool {return a.u == b.u;}) << std::endl;
}

为什么此代码无法编译以及如何修复? 我相信从上下文中可以清楚 function 包含的内容应该做什么。

我收到以下错误:

main.cpp:32:18: error: no matching function for call to 'contains'
    std::cout << contains(vec, S(15,159.48), [](S& a, S& b) -> bool {return a.u == b.u;}) << std::endl;
                 ^~~~~~~~
main.cpp:14:27: note: candidate template ignored: could not match 'bool (*)(const T &, const T &)' against '(lambda at main.cpp:32:46)'
template<typename T> bool contains(std::vector<T> vec, T elem, bool equivalent(const T&, const T&))

它可以通过为第三个参数添加std::type_identity_t来修复,这样它就不会参与推导模板参数:

#include <iostream>
#include <vector>
#include <type_traits>

struct S
{
    unsigned u;
    float f;

    S(unsigned u0, float f0) : u(u0), f(f0) {}
};

template<typename T> bool contains(std::vector<T> vec, T elem, std::type_identity_t<bool (*)(const T&, const T&)> equivalent)
// Can also use following synaxis: std::type_identity_t<bool(const T&, const T&)> *equivalent
{
    // some implementation
}

int main() {
    std::vector<S> vec{{15, 17.8}};

    std::cout << contains(vec, S(15,159.48), [](const S& a, const S& b) -> bool {return a.u == b.u;}) << std::endl;
}

如果你没有 C++20,你可以很容易地自己定义它:

template< class T >
struct type_identity {
    using type = T;
};

template< class T >
using type_identity_t = typename type_identity<T>::type;

为什么此代码无法编译以及如何修复? 我相信从上下文中可以清楚 function 包含的内容应该做什么。

问题是 lambda 不是 function; 它是一个 object,里面有一个 function ( operator() )。

在你的情况下,假设 lambda 没有捕获,可以转换为 function 指针。

那么为什么编译器不将 lambda 转换为 function 指针呢? 还有一个问题; 一种先有鸡还是先有蛋的问题: T模板类型的推导。

模板contains()

template <typename T>
bool contains(std::vector<T> vec, T elem, bool equivalent(const T&, const T&))

对所有 arguments 使用T类型:对于vec ,对于elem和对于equivalent 所以编译器尝试从所有 arguments 中推导出T

鉴于编译器试图推导T也形成第三个参数(鸡和蛋的问题)不能从 lambda 推导T因为 lambda 不是 function 指针并且不能将 lambda 转换为 function 指针因为不t 从 lambda 推导出T

我看到四种可能的解决方案。

  1. 第一个是sklott答案中的解决方案:定义contains() ,使用std::type_identity ,如下
template <typename T>
bool contains (std::vector<T> vec, T elem, std::type_identity_t<bool(*)(const T&, const T&)> equivalent) 

或者也如下

template<typename T>
bool contains (std::vector<T> vec, T elem, std::type_identity_t<bool(const T&, const T&)> equivalent)

通过这种方式,您可以禁止从equivalent推导T ,从vecelem推导(作为T ),因此编译器可以将S转换为 function 或 function 指针。

  1. 为 function 或功能使用另一个模板类型名称(如评论中所建议的)
template <typename T, typename F>
bool contains (std::vector<T> vec, T elem, F equivalent)

这种方式equivalent可以直接是一个lambda,不用转换。 所以你也可以接受捕获 lambda (不能转换为函数)。 缺点是你不能强制那个equivalent接收两个T const & arguments,但是使用equivalent应该足以检查不一致

  1. 您可以不修改contains() function,但您可以更改调用,使用 lambda 前面的+运算符,强制将 lambda 转换为 function 指针
// .........................V  the '+' operator force the conversion
contains(vec, S(15,159.48), +[](S const & a, S const & b)... 

但请记住定义 lambda 接收两个S const & ,如 function 定义,而不是两个S & ,如您的示例。

通过这种方式,编译器接收到一个 function 指针,也可以从第三个参数正确地推断出TS

  1. 您可以不修改contains() function 并在调用中显式显示模板类型
// .....VVV  S type is explicit, no type deduction take place
contains<S>(vec, S(15,159.48), [](S const & a, S const & b)... 

这样调用就明确了S类型,对于T 所以没有类型推导发生,所以编译器可以将 lambda 转换为 function

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM