无法推断函数 return 类型
Cannot deduce function return type
这不适用于 gcc-10 或 clang-10。
template <typename R, typename T>
auto invoke_function(R (&f)(T), T t) { return std::invoke(f, t); }
invoke_function(std::to_string, 42);
这适用于 gcc-10,但不适用于 clang-10。
template <typename R, typename T>
auto invoke_function(T t, R (&f)(T)) { return std::invoke(f, t); }
invoke_function(42, std::to_string);
所有情况下的错误消息都非常相似:“无法推断模板参数 'R'”或“无法推断模板参数‘R’”(gcc)。
尚不清楚此代码被拒绝的原因。由于推导了T
,所以可以确定std::to_string
的重载。对参数顺序的依赖特别烦人。它不应该正常工作吗?
我知道这个问题可以通过引入一个函数对象来回避:
struct to_string
{
template<typename T> std::string operator()(T t) { return std::to_string(t); }
};
然后只使用 std::invoke
就可以了。然而,这需要为每个重载集创建一个单独的函数对象。
有没有更好的方法?
It isn't clear why this code is rejected. Since T
is deduced, the overload of std::to_string
can be determined.
实际情况并非如此。模板推导首先推导每个 parameter/argument 对 独立 - 然后我们将所有推导放在一起并确保它们是一致的。所以我们从 42
推导出 T
,然后,我们分别从 std::to_string
推导出 R(&)(T)
。但是 std::to_string
的每个重载都匹配该模式,所以我们不知道该选择哪个。
但只有当我们可以独立地推导出每一对时,以上内容才成立。如果一个参数是non-deducible,我们跳过它,然后尝试返回并稍后填充它。这就是这里的关键 - 我们重组推导,这样我们 仅 从 42
:
推导出 T
template <typename T>
auto invoke_function(std::string (&f)(std::type_identity_t<T>), T t) { return std::invoke(f, t); }
在这里,我们推导出 T
和 int
,现在我们从 std::to_string
推导出 std::string(&)(int)
。现在有效,因为只有一个重载匹配该模式。
除了现在这是未定义的行为,根据 [namespace.std]/6:
Let F
denote a standard library function ([global.functions]), a standard library static member function, or an instantiation of a standard library function template. Unless F
is designated an addressable function, the behavior of a C++ program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer to F
.
std::to_string
不是可寻址函数。
所以 真正的 更好的方法是将 to_string
包装在 lambda 中并传递它:
invoke_function([](auto x){ return std::to_string(x); }, 42);
并且只是调整 invoke_function
以接受任意可调用而不是特定的函数。 lambda 包装概括为:
#define FWD(x) static_cast<decltype(x)&&>(x)
#define LIFT(name) [&](auto&&... args) noexcept(noexcept(name(FWD(args)...))) -> decltype(name(FWD(args)...)) { return name(FWD(args)...); }
invoke_function(LIFT(std::to_string), 42);
这不适用于 gcc-10 或 clang-10。
template <typename R, typename T>
auto invoke_function(R (&f)(T), T t) { return std::invoke(f, t); }
invoke_function(std::to_string, 42);
这适用于 gcc-10,但不适用于 clang-10。
template <typename R, typename T>
auto invoke_function(T t, R (&f)(T)) { return std::invoke(f, t); }
invoke_function(42, std::to_string);
所有情况下的错误消息都非常相似:“无法推断模板参数 'R'”或“无法推断模板参数‘R’”(gcc)。
尚不清楚此代码被拒绝的原因。由于推导了T
,所以可以确定std::to_string
的重载。对参数顺序的依赖特别烦人。它不应该正常工作吗?
我知道这个问题可以通过引入一个函数对象来回避:
struct to_string
{
template<typename T> std::string operator()(T t) { return std::to_string(t); }
};
然后只使用 std::invoke
就可以了。然而,这需要为每个重载集创建一个单独的函数对象。
有没有更好的方法?
It isn't clear why this code is rejected. Since
T
is deduced, the overload ofstd::to_string
can be determined.
实际情况并非如此。模板推导首先推导每个 parameter/argument 对 独立 - 然后我们将所有推导放在一起并确保它们是一致的。所以我们从 42
推导出 T
,然后,我们分别从 std::to_string
推导出 R(&)(T)
。但是 std::to_string
的每个重载都匹配该模式,所以我们不知道该选择哪个。
但只有当我们可以独立地推导出每一对时,以上内容才成立。如果一个参数是non-deducible,我们跳过它,然后尝试返回并稍后填充它。这就是这里的关键 - 我们重组推导,这样我们 仅 从 42
:
T
template <typename T>
auto invoke_function(std::string (&f)(std::type_identity_t<T>), T t) { return std::invoke(f, t); }
在这里,我们推导出 T
和 int
,现在我们从 std::to_string
推导出 std::string(&)(int)
。现在有效,因为只有一个重载匹配该模式。
除了现在这是未定义的行为,根据 [namespace.std]/6:
Let
F
denote a standard library function ([global.functions]), a standard library static member function, or an instantiation of a standard library function template. UnlessF
is designated an addressable function, the behavior of a C++ program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer toF
.
std::to_string
不是可寻址函数。
所以 真正的 更好的方法是将 to_string
包装在 lambda 中并传递它:
invoke_function([](auto x){ return std::to_string(x); }, 42);
并且只是调整 invoke_function
以接受任意可调用而不是特定的函数。 lambda 包装概括为:
#define FWD(x) static_cast<decltype(x)&&>(x)
#define LIFT(name) [&](auto&&... args) noexcept(noexcept(name(FWD(args)...))) -> decltype(name(FWD(args)...)) { return name(FWD(args)...); }
invoke_function(LIFT(std::to_string), 42);