【发布时间】:2020-06-20 10:13:40
【问题描述】:
我正在尝试为 std::invoke 提供一个包装器,以完成推断函数类型的工作,即使在函数重载时也是如此。
(我昨天向related question 询问了可变参数和方法指针版本)。
当函数有一个参数时,此代码 (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));
}
对于更多的输入参数显然需要减少代码膨胀,但这是一个单独的问题。
如果用户的重载仅因 const/references 不同,如下所示:
#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);
}
然后Invoke推导函数不正确,有g++输出:
(int &)
(const int &)(int &&)
(const int &)
还有clang++:
(int &)
(const int &)(int &&)
(int &&)
(感谢 geza 指出 clang 的输出不同)。
所以Invoke 具有未定义的行为。
我怀疑元编程将是解决这个问题的方法。无论如何,是否可以在Invoke 站点正确处理类型推导?
【问题讨论】:
-
预期输出是什么?是 (int&) (int&&) 吗?
-
@L.F.,是的。这些是 Foo 的输出,所以它们也应该是 Invoke 的输出。
-
对我来说,clang 给出了不同的结果:对于第二种情况,它会打印两次
(int &&)。 -
肯定和
S参数推导有关。尝试注释掉Invoke的const T &版本并注意错误。此外,如果显式提供了参数 (Invoke<void>(&Foo, num)),则会调用正确的版本。 -
这是第一种情况的理论:当编译器考虑非常量
Invoke时,它可以同时使用常量和非常量Foo对其进行实例化。而且它不会检查两者的返回类型 (S) 是否相同,因此它说它不能推断出S。所以它忽略了这个模板。虽然实例化 constInvoke只能使用 constFoo来完成,所以在这种情况下它可以推导出S。因此编译器使用这个模板。
标签: c++ templates function-pointers template-meta-programming type-deduction