简体   繁体   English

传递重载函数指针及其参数时的错误类型推导

[英]Bad type deduction when passing overloaded function pointer and its arguments

I'm trying to provide a wrapper around std::invoke to do the work of deducing the function type even when the function is overloaded.我正在尝试为std::invoke提供一个包装器,以完成推导函数类型的工作,即使函数已重载。
(I asked a related question yesterday for the variadic and method pointer version). (我昨天为可变参数和方法指针版本问了一个相关问题)。

When the function has one argument this code (C++17) works as expected under normal overload conditions:当函数有一个参数时,此代码 (C++17) 在正常重载条件下按预期工作:

#include <functional>

template <typename ReturnType, typename ... Args>
using FunctionType = ReturnType (*)(Args...);

template <typename S, typename T>
auto Invoke (FunctionType<S, T> func, T arg)
{   
    return std::invoke(func, arg);
}

template <typename S, typename T>
auto Invoke (FunctionType<S, T&> func, T & arg)
{   
    return std::invoke(func, arg);
}

template <typename S, typename T>
auto Invoke (FunctionType<S, const T&> func, const T & arg)
{
    return std::invoke(func, arg);
}

template <typename S, typename T>
auto Invoke (FunctionType<S, T&&> func, T && arg)
{   
    return std::invoke(func, std::move(arg));
}

Reducing the code bloat is obviously needed for more input arguments, but that's a separate problem.更多的输入参数显然需要减少代码膨胀,但这是一个单独的问题。

If the user has overloads differing only by const/references, like so:如果用户的重载仅因常量/引用而不同,如下所示:

#include <iostream>

void Foo (int &)
{
    std::cout << "(int &)" << std::endl;
}

void Foo (const int &)
{
    std::cout << "(const int &)" << std::endl;
}

void Foo (int &&)
{
    std::cout << "(int &&)" << std::endl;
}

int main()
{
    int num;
    Foo(num);
    Invoke(&Foo, num);

    std::cout << std::endl;

    Foo(0);
    Invoke(&Foo, 0);
}

Then Invoke deduces the function incorrectly, with g++ output:然后Invoke错误地推断出该函数,输出为 g++:

(int &) (int &)
(const int &) (const int &)

(int &&) (整数&&)
(const int &) (const int &)

And clang++:和叮当++:

(int &) (int &)
(const int &) (const int &)

(int &&) (整数&&)
(int &&) (整数&&)

(Thanks to geza for pointing out that clang's outputs were different). (感谢 geza 指出 clang 的输出不同)。

So Invoke has undefined behaviour.因此Invoke具有未定义的行为。

I suspect that metaprogramming would be the way to approach this problem.我怀疑元编程将是解决这个问题的方法。 Regardless, is it possible to handle the type deduction correctly at the Invoke site?无论如何,是否可以在Invoke站点正确处理类型推导?

Theory理论

For each function template Invoke , the template argument deduction (that must succeed for overload resolution to consider it) considers each Foo to see whether it can deduce however many template parameters (here, two) for the one function parameter ( func ) involved.对于每个函数模板Invoke ,模板参数推导(必须成功才能考虑重载决议)考虑每个Foo以查看它是否可以为所涉及的一个函数参数( func )推导出多个模板参数(此处为两个)。 The overall deduction can succeed only if exactly one Foo matches (because otherwise there is no way to deduce S ).只有当恰好有一个Foo匹配时,整体推导才能成功(因为否则无法推导S )。 (This was more or less stated in the comments.) (这或多或少在评论中有所说明。)

The first (“by value”) Invoke never survives: it can deduce from any of the Foo s.第一个(“按值”) Invoke永远不会存活:它可以从任何Foo推导出来。 Similarly, the second (“non- const reference”) overload accepts the first two Foo s.类似地,第二(“非const参考”)过载接受前两个Foo秒。 Note that these apply regardless of the other argument to Invoke (for arg )!请注意,无论Invoke的其他参数如何(对于arg ),这些都适用!

The third ( const T& ) overload selects the corresponding Foo overload and deduces T = int ;第三个( const T& )重载选择相应的Foo重载并推导出T = int the last does the same thing with the last overload (where T&& is a normal rvalue reference), and therefore rejects lvalue arguments despite its universal reference (which deduces T as int& (or const int& ) in that case and conflicts with func 's deduction).最后一个与最后一个重载(其中T&&是正常的右值引用)做同样的事情,因此拒绝左值参数,尽管它的通用引用(在这种情况下将T推导出为int& (或const int& )并与func的推导冲突)。

Compilers编译器

If the argument for arg is an rvalue (and, as usual, isn't const), both plausible Invoke overloads succeed at deduction, and the T&& overload should win (because it binds an rvalue reference to an rvalue ).如果arg是一个右值(并且像往常一样,不是 const),那么两个合理的Invoke重载在推导时都会成功,并且T&&重载应该获胜(因为它将右值引用绑定到一个rvalue )。

For the case from the comments:对于评论中的案例:

template <typename U>
void Bar (U &&);
int main() {
  int num;
  Invoke<void>(&Bar, num);
}

No deduction takes place from &Bar since a function template is involved, so T is successfully deduced (as int ) in every case.由于涉及函数模板,因此不会从&Bar推导,因此在每种情况下都成功推导了T (作为int )。 Then, deduction happens again for each case to identify the Bar specialization (if any) to use, deducing U as fail , int& , const int& , and int& respectively.然后,对每种情况再次进行推导,以确定要使用的Bar (如果有),分别推导出Ufailint&const int&int& The int& cases are identical and plainly better, so the call is ambiguous. int&情况是相同的,而且明显更好,所以调用是模棱两可的。

So Clang is right here.所以Clang就在这里。 (But there's no “undefined behavior” here.) (但这里没有“未定义的行为”。)

Solution解决方案

I don't have a general answer for you;我没有给你一个通用的答案; since certain parameter types can accept multiple value-category/const-qualification pairs, it's not going to be easy to emulate overload resolution correctly in all such cases.由于某些参数类型可以接受多个值类别/常量限定对,因此在所有这些情况下都不容易正确模拟重载解析。 There have been proposals to reify overload sets in one way or another;已经有人提议以某种方式具体化重载集; you might consider one of the current techniques along those lines (like a generic lambda per target function name).您可能会考虑这些方面的当前技术之一(例如每个目标函数名称的通用 lambda )。

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

相关问题 将函数指针传递给函数时处理<unresolved overloaded function type> - Dealing with <unresolved overloaded function type> when passing function pointer to function 从函数指针的参数推导出C ++模板类型 - C++ template type deduction from arguments of a function pointer 成员函数指针的类型推导 - Type deduction for a member function pointer 为什么在传递 long long 时会调用一个重载的 function 和两个双精度类型的 arguments? - Why is an overloaded function with two arguments of type double called when passing a long long? 将lambda传递给variadic std :: function时的类型推导 - Type deduction when passing lambda into variadic std::function 涉及非推导参数包的函数指针的参数类型的模板参数推导 - Template arguments deduction for parameter type of function pointer involving non-deduced parameter pack 将成员函数指针传递给采用带有可变参数的MFP的函数时,类型不匹配 - Type mismatch when passing member function pointer to a function that takes MFP with variadic arguments 成员变量和函数参数的模板类型推导 - Template type deduction for member variables and function arguments 参数包和成员函数指针类型推导 - Parameter pack and member function pointer type deduction 从函数指针作为模板参数进行类型推导 - type deduction from a function pointer as template argument
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM