简体   繁体   English

“候选模板被忽略:无法匹配......”用于模板函数的模板函数参数

[英]"candidate template ignored: could not match ..." for template function argument to template function

overview概述

i am attempting to pass a lambda to a template function which takes a template function type.我试图将 lambda 传递给采用模板函数类型的模板函数。 on compilation is errors with candidate template ignored: could not match... .编译时出现错误, candidate template ignored: could not match...

however, when i try to pass the same lambda to a template class which takes a template function type, it compiles and works.但是,当我尝试将相同的 lambda 传递给采用模板函数类型的模板类时,它会编译并工作。

questions问题

  1. why does this work for a template class but not a template function?为什么这适用于模板类而不适用于模板函数?
  2. is there a way to make this work?有没有办法让这项工作?
  3. what is the extra expected parameter to the template function type when i try this with the template function (see below for more details)?当我用模板函数尝试这个时,模板函数类型的额外预期参数是什么(更多细节见下文)?

code代码

consider the following code (c++17)考虑以下代码 (c++17)

#include <functional>

// define the template function type
template<typename T, typename...S>
using compose_fn_t = std::function<T(S...)>;


// define a template function which accepts the template function type
template<typename T, typename... S>
void compose(compose_fn_t<T, S...> fn) {};

// define a template class which accepts the template function type
template<typename T, typename... S>
class Compose {
public:
  Compose(compose_fn_t<T, S...> fn) {};
};

// some arbitrary types
struct A{};
struct B{};
struct C{};

int main() {

  compose<A, B, C>
    ([] (B b, C c) -> A { return {}; });  // this will not compile!

  Compose<A, B, C>
    ([] (B b, C c) -> A { return {}; });  // this compiles and runs correctly!

  return 0;
}

when i compile using compose<A, B, C> , it throws the following error当我使用compose<A, B, C>编译时,它会抛出以下错误

$ g++ -std=c++17 -o main main.cpp                                                                                                  
main.cpp:18:3: error: no matching function for call to 'compose'
  compose<A, B, C>
  ^~~~~~~~~~~~~~~~
main.cpp:8:6: note: candidate template ignored: could not match 'function<A (B, C, type-parameter-0-1...)>' against '(lambda at main.cpp:19:6)'
void compose(compose_fn_t<T, S...> fn) {
     ^
1 error generated.

what is this additional type-parameter-0-1 type that is expected by the template function type ( compose_fn_t )?模板函数类型 ( compose_fn_t ) 所期望的这个额外的type-parameter-0-1类型是什么?

If you specify a template argument list for a function call, as in compose<A, B, C> , then this list is considered partial if there are more template parameters than arguments.如果您为函数调用指定模板参数列表,如compose<A, B, C> ,那么如果模板参数多于参数,则该列表被视为部分列表。

Calling the function still does template argument deduction on the remaining template parameters.调用该函数仍然对剩余的模板参数进行模板参数推导。

In your case the remaining parameters of the parameter pack are deduced against the compose_fn_t parameter (with the first three template arguments already determined), but that fails, because the lambda cannot be deduced to a std::function type.在您的情况下,参数包的其余参数是根据compose_fn_t参数(已经确定前三个模板参数)推导出来的,但是失败了,因为无法将 lambda 推导出为std::function类型。

You need to force the template parameters used in the function parameter into a non-deduced context in order to avoid this.您需要将函数参数中使用的模板参数强制转换为非推导上下文以避免这种情况。 One way is to use一种方法是使用

template<typename T, typename... S>
void compose(typename std::type_identity<compose_fn_t<T, S...>>::type fn) {};

since everything to the left-hand side of the scope resolution operator :: is non-deduced.因为作用域解析运算符::左侧的所有内容都是非推导的。 This does however also mean, that you will not be able to call the function without a template argument list.然而,这也意味着,如果没有模板参数列表,您将无法调用该函数。

std::type_identity is a C++20 feature, but you can easily implement your own. std::type_identity是 C++20 特性,但您可以轻松实现自己的特性。 It does nothing but return the type given to it in its type member:它只返回在其type成员中赋予它的type

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

Alternatively take the argument by forwarding reference and forward it into a std::function construction in the body of the function, avoiding any deduction:或者通过转发引用来获取参数,并将其转发到std::function体中的std::function结构中,避免任何推论:

template<typename T, typename... S, typename F>
void compose(F&& f) {
    compose_fn_t<T, S...> fn{std::forward<F>(f)};
    // Use fn as before
};

This is not a problem with the class template, because class template argument deduction (CTAD) is only performed if no template argument list is provided at all.这不是类模板的问题,因为类模板参数推导 (CTAD) 仅在根本没有提供模板参数列表时才执行。


You can also use CTAD on std::function to choose the correct std::function specialization for you, without having to repeat the types:您还可以在std::function上使用 CTAD 为您选择正确的std::function专业化,而无需重复类型:

compose(std::function{[] (B b, C c) -> A { return {}; }});

You can also move this construction into the compose definition:您还可以将此构造移动到compose定义中:

template<typename F>
void compose(F&& f) {
    auto fn = std::function{std::forward<F>(f)};
    // use fn here as before
};

so that the call以便调用

compose([] (B b, C c) -> A { return {}; });

is sufficient.足够了。

Note that both of these cases don't work with generic lambdas and also note that they don't work if you replace std::function with your compose_fn_t alias, because CTAD is not done on aliases.请注意,这两种情况都不适用于通用 lambda,还要注意,如果您将std::function替换为compose_fn_t别名,它们将compose_fn_t ,因为compose_fn_t别名上完成。

In the class case ( Compose<A, B, C> ) no deduction takes place when/after you specify the template arguments.在类情况 ( Compose<A, B, C> ) 中,在指定模板参数时/之后不会发生任何推论。 We have T = A and ...S = B, C .我们有T = A...S = B, C Everything works as expected.一切都按预期工作。

But in the function case ( compose<A, B, C>(...) ), the compiler doesn't know whether these are all the template arguments or whether it is supposed to deduce additional ones.但是在函数情况下( compose<A, B, C>(...) ),编译器不知道这些是所有模板参数还是应该推导出额外的参数。 You can glean this from the error: It mentions function<A (B, C, type-parameter-0-1...)> , ie it doesn't stop after C .您可以从错误中收集到这一点:它提到了function<A (B, C, type-parameter-0-1...)> ,即它不会在C之后停止。

This difference becomes clear when you attempt to pass a std::function taking three arguments:当您尝试传递带有三个参数的std::function时,这种差异变得很明显:

std::function<A(B, C, D)> f;
compose<A, B, C>(f);

Here, the compiler deduces compose<A, B, C, D> , ie T = A and ...S = B, C, D .在这里,编译器推导出compose<A, B, C, D> ,即T = A...S = B, C, D This compiles because the compiler tries to deduce additional arguments for compose 's S pack.这是因为编译器试图为composeS包推导出额外的参数。

Compose<A, B, C>(f);

This does not compile: f does not match compose_fn_t<A, B, C> .这不会编译: f不匹配compose_fn_t<A, B, C> The compiler doesn't perform deduction of the Compose template arguments.编译器不执行Compose模板参数的推导。

Your problem is about the implicit conversion in c++.您的问题是关于 C++ 中的隐式转换。

Replace:代替:

compose<A, B, C>([] (B b, C c) -> A { return {}; });  // this will not compile!

With:和:

compose<A, B, C>((compose_fn_t<A, B, C>)([] (B b, C c) -> A { return {}; }));  // this will compile!

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

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