啊呀,总算是把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
表达式中有两个子表达式:
std::declval<T>().foo()
:这个子表达式用于检查T
类型是否有名为foo
的成员函数,并尝试获取其返回类型。但是,由于逗号运算符的特性,这个子表达式的实际类型在decltype
的最终结果中是被忽略的。std::true_type()
:这是第二个子表达式,它返回std::true_type
类型的对象。由于它是逗号运算符后面的最后一个表达式,decltype
将采用它的类型作为整个表达式的类型。
因此,整个 decltype
表达式的类型是 std::true_type
,这是逗号运算符后最后一个表达式的类型。这种写法是 SFINAE(Substitution Failure Is Not An Error)技术的一部分,用于在编译时根据类型特性进行条件判断。