从模板类型而不是参数获取转发类型
Get forwarded type from the template type and not the argument
考虑以下函数:
template <class T>
constexpr /* something */ f(T&& x) {
// do something
}
假设我想根据传递给名为 myfunction
的函数的转发参数的类型来执行 sfinae。实现此目的的一种方法是:
template <class T>
constexpr auto f(T&& x) -> decltype(myfunction(std::forward<T>(x))) {
// do something
}
有没有办法在模板级别执行此操作:
// This code won't compile
template <class T, class R = decltype(myfunction(std::forward<T>(x)))>
constexpr R f(T&& x) {
// do something
}
只是我还没有访问 x
的权限,所以这段代码无法编译。有没有办法仅基于T
(可能使用std::declval
)来实现这一点?
注意:这不是 X/Y 问题,它只是一个示例来说明这种情况发生的位置:我不知道如何在不访问变量的情况下通过转发来执行 SFINAE,因为对我来说std::forward
还是有点神秘
是的,std::declval
确实是关键:
template <class T, class R = decltype(myfunction(std::declval<T>()))>
constexpr R f(T&& x) {
// do something
}
这将要么 SFINAE 输出,要么 R
将成为 return 类型的 myfunction
选择的重载。
你的问题让我觉得你需要复习一下引用折叠的工作原理;我建议阅读该领域的内容(this 似乎是一个很好的起点)。
std::forward()
与本题无关。 constexpr
也没有。因此,让我们从最基本的函数开始,该函数只是 return 按值传递其参数:
template <class T>
auto f(T x) -> decltype(x);
当然,我们可以直接使用 T
,但这太简单了。现在的问题是,我们如何在保持 SFINAE 的同时将其提升到模板参数中(假设这里显然没有可能的替换失败,但请耐心等待):
template <class T, class R = decltype(x)>
R f(T x);
所以这不起作用,因为还没有 x
(或者更糟的是,名称查找在其他地方找到了一些不相关的 x
)。我们可以在 trailing-return-type 中使用参数名称,因为它们在范围内,但我们不能在默认模板参数中将它们用作 expression-SFINAE 的一部分,因为它们尚未在范围内。
但是因为这些是模板参数,所以我们不关心它们的值。我们只关心他们的类型。这不是一个计算表达式。所以我不需要 x
,我需要和 x
类型相同的东西。第一步可能是:
template <class T, class R = decltype(T{})>
R f(T x);
这可行...只要 T
是默认构造的。但我正在编写一个模板,我不想对我不需要的类型做出假设。因此,我可以做类似的事情:
template <class T> T make(); // never defined
template <class T, class R = decltype(make<T>())>
R f(T x);
现在我们有了 T
类型的任意表达式,我们可以在 decltype
中使用默认模板参数。虽然,我们 仍然 有点受 make()
的限制 - 有些类型你不能 return 按函数的值(例如数组),所以事实证明添加引用更有用。我们需要一个比 make
:
更不容易碰撞的名字
template <class T>
add_rvalue_reference<T> declval();
template <class T, class R = decltype(declval<T>())>
R f(T x);
这正是 std::declval<T>
的要点 - 在未计算的上下文中为您提供类型 T
的表达式。
回到你原来的问题。我们使用如何从 decltype(x)
到 decltype(declval<T>())
的相同思维过程,但只是将其应用于不同的表达式。我们有 myfunction(std::forward<T>(x))
而不是 x
。也就是说,我们调用 myfunction
的类型与我们的参数相同:
template <class T, class R = decltype(myfunction(std::declval<T&&>()))>
R f(T&& x);
但是由于引用折叠规则,std::declval<T&&>
实际上和std::declval<T>
是一个函数,所以我们可以写成:
template <class T, class R = decltype(myfunction(std::declval<T>()))>
R f(T&& x);
考虑以下函数:
template <class T>
constexpr /* something */ f(T&& x) {
// do something
}
假设我想根据传递给名为 myfunction
的函数的转发参数的类型来执行 sfinae。实现此目的的一种方法是:
template <class T>
constexpr auto f(T&& x) -> decltype(myfunction(std::forward<T>(x))) {
// do something
}
有没有办法在模板级别执行此操作:
// This code won't compile
template <class T, class R = decltype(myfunction(std::forward<T>(x)))>
constexpr R f(T&& x) {
// do something
}
只是我还没有访问 x
的权限,所以这段代码无法编译。有没有办法仅基于T
(可能使用std::declval
)来实现这一点?
注意:这不是 X/Y 问题,它只是一个示例来说明这种情况发生的位置:我不知道如何在不访问变量的情况下通过转发来执行 SFINAE,因为对我来说std::forward
还是有点神秘
是的,std::declval
确实是关键:
template <class T, class R = decltype(myfunction(std::declval<T>()))>
constexpr R f(T&& x) {
// do something
}
这将要么 SFINAE 输出,要么 R
将成为 return 类型的 myfunction
选择的重载。
你的问题让我觉得你需要复习一下引用折叠的工作原理;我建议阅读该领域的内容(this 似乎是一个很好的起点)。
std::forward()
与本题无关。 constexpr
也没有。因此,让我们从最基本的函数开始,该函数只是 return 按值传递其参数:
template <class T>
auto f(T x) -> decltype(x);
当然,我们可以直接使用 T
,但这太简单了。现在的问题是,我们如何在保持 SFINAE 的同时将其提升到模板参数中(假设这里显然没有可能的替换失败,但请耐心等待):
template <class T, class R = decltype(x)>
R f(T x);
所以这不起作用,因为还没有 x
(或者更糟的是,名称查找在其他地方找到了一些不相关的 x
)。我们可以在 trailing-return-type 中使用参数名称,因为它们在范围内,但我们不能在默认模板参数中将它们用作 expression-SFINAE 的一部分,因为它们尚未在范围内。
但是因为这些是模板参数,所以我们不关心它们的值。我们只关心他们的类型。这不是一个计算表达式。所以我不需要 x
,我需要和 x
类型相同的东西。第一步可能是:
template <class T, class R = decltype(T{})>
R f(T x);
这可行...只要 T
是默认构造的。但我正在编写一个模板,我不想对我不需要的类型做出假设。因此,我可以做类似的事情:
template <class T> T make(); // never defined
template <class T, class R = decltype(make<T>())>
R f(T x);
现在我们有了 T
类型的任意表达式,我们可以在 decltype
中使用默认模板参数。虽然,我们 仍然 有点受 make()
的限制 - 有些类型你不能 return 按函数的值(例如数组),所以事实证明添加引用更有用。我们需要一个比 make
:
template <class T>
add_rvalue_reference<T> declval();
template <class T, class R = decltype(declval<T>())>
R f(T x);
这正是 std::declval<T>
的要点 - 在未计算的上下文中为您提供类型 T
的表达式。
回到你原来的问题。我们使用如何从 decltype(x)
到 decltype(declval<T>())
的相同思维过程,但只是将其应用于不同的表达式。我们有 myfunction(std::forward<T>(x))
而不是 x
。也就是说,我们调用 myfunction
的类型与我们的参数相同:
template <class T, class R = decltype(myfunction(std::declval<T&&>()))>
R f(T&& x);
但是由于引用折叠规则,std::declval<T&&>
实际上和std::declval<T>
是一个函数,所以我们可以写成:
template <class T, class R = decltype(myfunction(std::declval<T>()))>
R f(T&& x);