c++ 为什么std::declval不能直接替换为std::add_rvalue_reference< T>::type

gijlo24d  于 2023-05-02  发布在  其他
关注(0)|答案(3)|浏览(111)

根据https://en.cppreference.com/w/cpp/utility/declvaldeclval的可能实现如下:

template<typename T>
constexpr bool always_false = false;
 
template<typename T>
typename std::add_rvalue_reference<T>::type declval() noexcept
{
    static_assert(always_false<T>, "declval not allowed in an evaluated context");
}

我试图用std::add_rvalue_reference<Test>::type替换std::declval<T>()的用例,但它无法编译。为什么会这样呢?
以下是我的测试代码片段:

#include <iostream>
#include <type_traits>
#include <utility>

struct Test {
    int func() {
        return 1;
    }
};

template<typename T>
constexpr bool always_false = false;
 
template<typename T>
typename std::add_rvalue_reference<T>::type Decltype() noexcept
{
    static_assert(always_false<T>, "declval not allowed in an evaluated context");
}

int main() {
    if (std::is_same_v<decltype(std::add_rvalue_reference<Test>::type.func()),int>) {
        std::cout <<"same\n";
    } else {
        std::cout <<"different\n";
    }
}
fnx2tebb

fnx2tebb1#

你需要一个type的示例来检查非static成员函数func返回什么:

if (std::is_same_v<decltype(std::add_rvalue_reference<Test>::type{}.func()),int>)
//                                                               ^^

if constexpr (std::is_same_v<decltype(std::add_rvalue_reference_t<Test>{}.func()),int>)
//                                                                     ^^

但是,除非type是默认的可构造和可破坏的,否则这将不起作用。你可以使用自己的Decltype函数模板来解决这个问题:

std::is_same_v<decltype(Decltype<Test>().func()),int>

或者,以Test*开头:

std::is_same_v<decltype(static_cast<Test*>(nullptr)->func()),int>

// or if you really want an rvalue reference:

std::is_same_v<decltype(static_cast<Test&&>(*static_cast<Test*>(nullptr)).func()),int>
// or
std::is_same_v<decltype(std::move(*static_cast<Test*>(nullptr)).func()),int>

注:Decltype是一个误导性的名称。Declval更有意义。

ddarikpa

ddarikpa2#

问题decltype(std::add_rvalue_reference<Test>::type类型,而您试图调用该类型的函数,这是不允许的。
成员访问operator.将一个类对象作为其左操作数,但在您的示例中并非如此。

解决这个问题,可以创建结果类型的对象,然后使用该对象调用成员函数。

//--------------------------------------------VV---------->create an object of the resulting type and then call the member function on that object
decltype(std::add_rvalue_reference<Test>::type{}.func())
fsi0uk1n

fsi0uk1n3#

decltype(unevaluded_expresssion)应该给予其操作数的编译时类型-而不计算(评估)操作数。因此,unevaluded_expresssion应该是一个表达式,结果是一个值(与类型相反),但它实际上并没有作为使用decltype的结果进行计算。
现在,我们如何告诉编译器表达式应该有一个由特定类型假设,未指定/一般值组成的子表达式?
假设要求编译器“查找结果类型of类型为C的对象添加到类型为B的对象,并将其命名为my_type**。假设我们将在隐式会话之后包括加法到适当类型。
你是怎么作曲的?第一次尝试:

//Will this compile?!
using my_type = decltype (B{}+C{});

上面的问题是它要求BC都是默认可构造的。如果它们中的一个或两个都缺少默认构造函数怎么办?
如果这两种类型的属性都是已知的,则可以尝试查找有效的表达式来生成这两种类型的值。如果类型BC在模板定义中作为依赖/参数类型出现,该怎么办?你不再知道如何构造一个表达式来得到正确类型的值了吗?!
更好的尝试是:

using my_type = decltype (std::declval<B>() + std::declval<C>());

std::declval<X>()仅表示类型X虚构值。如果没有像std::declval这样的东西,你就无法表达这一点。requires子句是唯一的例外,但这是另一个非常长的故事。

相关问题