std::declval详解[CPP template-4]

啊呀,总算是把CPP模板入门的基础知识讲完了,也是可以着手写一些有用的东西了(虽然现在可能看上去呆呆的程序)
今天介绍的是std::declval 这个模板的源代码比较简单,但是用处还是比较广泛的,是初学者学习模板思想的一个好例子

declval 是 “declare value” 的缩写。在 C++ 中,std::declval 是一个函数模板,用于声明一个类型 T 的右值引用。它的主要作用是在某些情况下,例如在模板编程中,可以通过 std::declval<T>() 来获取类型 T 的一个右值引用,而无需实际创建 T 的对象。

具体来说,std::declval<T>() 返回的是一个 T&& 类型的右值引用,可以用于表达式中,如在 decltype 表达式中,用于推导函数返回类型或者表达式的类型,而不需要实际构造 T 类型的对象。

例如,在 SFINAE(Substitution Failure Is Not An Error)技术中,std::declval 可以帮助进行类型推导,而不会引发实际对象的创建或者引用的操作,仅仅是为了类型检查或者模板实例化时的类型推导。

因此,std::declval 提供了一种轻量级的方式来声明类型 T 的右值引用,使得在某些编程场景下,特别是与模板相关的场合,编译器能够正确地推导出类型信息。

#include <iostream>
#include <typeindex>
#include <typeinfo>

class A {
public:
    A(int i) {
        std::cout << "A(i)" << std::endl;
    }

    int fun() {
        return 1;
    }
};

int main() {
    // 获取类型信息的方式改为使用 type_index 和 type_info
    std::cout << "YT = " << typeid(decltype(std::declval<A>())).name() << std::endl;
    std::cout << "Af = " << typeid(decltype(std::declval<A>().fun())).name() << std::endl;

    std::cout << "A = " << sizeof(std::declval<A>()) << std::endl;
    std::cout << "A = " << sizeof(A) << std::endl;

    return 0;
}

源码

其实很简单哦,没想到吧,看完感觉自己也能去写CPP标准库了是不是

template <typename T>
struct m_Rvalue
{
    using mtype = T&&;
    //返回T类型的右值引用
};

template <typename T>
typename m_Rvalue<T>::mtype mdeclval() noexcept;

应用:固定的成员检查

下面的成员用来检查输入的类型中是否有foo()的成员函数(你看到这里,心里可能会想:不是哥们(;′⌒`)这是你说的有用 啊?类里的成员我自己写的自己不知道啊?不知道我也可以翻看源代码啊。稍安无燥,首先,学习的过程就是这样嘛,你写的第一个hello world也没见用处多大啊,但是它对你的编程学习是不是意义非凡?还有,这个程序自己检查自己的构成,也是有用的,是类似“反射”,程序自己知道自己里面有什么,自己处于什么状态,这是个有用的编程技巧)。顺带一提这里有逗号搭建匹配空间的技术

#include <iostream>
#include <type_traits>

struct MyType {
    int foo();
};

template <typename T>
auto check_foo(int) -> decltype(std::declval<T>().foo(), std::true_type());

template <typename T>
auto check_foo(...) -> std::false_type;

int main() {
    std::cout << std::boolalpha;
    std::cout << "Has foo(): " << decltype(check_foo<MyType>(0))::value << std::endl;  // true
    std::cout << "Has foo(): " << decltype(check_foo<int>(0))::value << std::endl;     // false
    return 0;
}

decltype 使用了逗号运算符(,),这是一个顺序点,它允许在单个表达式中按顺序执行多个子表达式。逗号运算符的结果是最后一个子表达式的类型。在这个上下文中,decltype 表达式中有两个子表达式:

  1. std::declval<T>().foo():这个子表达式用于检查 T 类型是否有名为 foo 的成员函数,并尝试获取其返回类型。但是,由于逗号运算符的特性,这个子表达式的实际类型在 decltype 的最终结果中是被忽略的。
  2. std::true_type():这是第二个子表达式,它返回 std::true_type 类型的对象。由于它是逗号运算符后面的最后一个表达式,decltype 将采用它的类型作为整个表达式的类型。

因此,整个 decltype 表达式的类型是 std::true_type,这是逗号运算符后最后一个表达式的类型。这种写法是 SFINAE(Substitution Failure Is Not An Error)技术的一部分,用于在编译时根据类型特性进行条件判断。

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
std::bind是C++11引入的函数对象适配器,用于将函数对象(包括普通函数、成员函数、函数指针等)与其参数进行绑定,生成一个新的函数对象。 它的基本语法如下: ```cpp template< class Fn, class... Args > bind( Fn&& fn, Args&&... args ); ``` 其中,Fn表示要绑定的函数对象(可以是普通函数、成员函数、函数指针等),Args表示要绑定的参数。 使用std::bind可以实现参数的绑定、参数重排、占位符等功能。下面是一些示例: 1. 绑定普通函数: ```cpp #include <iostream> #include <functional> void foo(int a, int b) { std::cout << "a + b = " << a + b << std::endl; } int main() { auto func = std::bind(foo, 1, 2); func(); // 输出:a + b = 3 return 0; } ``` 2. 绑定成员函数: ```cpp #include <iostream> #include <functional> class Foo { public: void bar(int a, int b) { std::cout << "a + b = " << a + b << std::endl; } }; int main() { Foo obj; auto func = std::bind(&Foo::bar, &obj, 1, 2); func(); // 输出:a + b = 3 return 0; } ``` 3. 参数重排: ```cpp #include <iostream> #include <functional> void foo(int a, int b) { std::cout << "a + b = " << a + b << std::endl; } int main() { auto func = std::bind(foo, std::placeholders::_2, std::placeholders::_1); func(2, 1); // 输出:a + b = 3 return 0; } ``` 在这个示例中,使用std::placeholders::_1和std::placeholders::_2来表示占位符,表示在调用func时,第一个参数将会被传递给foo的第二个参数,第二个参数将会被传递给foo的第一个参数。 这些仅是std::bind的一些基本用法,它还提供了更多的功能和特性,可以根据具体需求进行学习和使用。希望能对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值