从模板类型而不是参数获取转发类型

时间:2017-03-12 06:14:17

标签: c++ c++14 template-meta-programming perfect-forwarding trailing-return-type

考虑以下功能:

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,因为对我来说{{1}的行为仍然有点神秘。

2 个答案:

答案 0 :(得分:5)

是的,std::declval确实是关键:

template <class T, class R = decltype(myfunction(std::declval<T>()))>
constexpr R f(T&& x) {
    // do something
}

这将是SFINAE out或R将是选择myfunction的任何重载的返回类型。

你的问题让我觉得你需要复习参考折叠的工作原理;我建议在该领域阅读(this似乎是一个很好的起点)。

答案 1 :(得分:4)

std::forward()与此问题无关。 constexpr也没有。因此,让我们从super basic开始,这是一个只通过值返回其参数的函数:

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中使用参数名称,因为它们在范围内,但是我们不能在默认模板参数中将它们用作表达式-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()有点限制 - 但有些类型您无法通过功能(例如数组)返回值,因此事实证明更有用的是添加引用。我们需要一个不太可能碰撞的名称而不是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>())相同的思维过程,但只是将其应用于不同的表达式。我们x取代myfunction(std::forward<T>(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); 功能相同,所以我们可以写一下:

echo "<ul class='logad' >
            <li class='o'>Olá," . $current_user->user_firstname . "</li>
            <li class='d'>
<a href='https://3brow.com/minha-conta' class='ver'>Ver minha conta</a>
<a href='https://3brow.com/minha-conta/Sair/' class='sair'>Sair</a></li>
        </ul>";
} else {
echo "<ul class='ent'>
            <li><a href='#''>Entrar</a></li>
            <li><a href='#''>Registrar</a></li>"
        . do_action( 'wooc_save_extra_register_fields' ) . "</ul>";
}