下面的代码来自 libstdc++-v3 std::type_traits
,这是 std::declval
的实现:
template<typename _Tp, typename _Up = _Tp&&> // template 1
_Up
__declval(int);
template<typename _Tp> // template 2
_Tp
__declval(long);
template<typename _Tp> // template 3
auto declval() noexcept -> decltype(__declval<_Tp>(0));
但我想我可以实现 declval
简单地说:template <typename T> T declval();
这是我的测试代码:#include <iostream>
using namespace std;
struct C {
C() = delete;
int foo() { return 0; }
};
namespace test {
template <typename T> T declval();
};// namespace test
int main() {
decltype(test::declval<C>().foo()) n = 1;
cout << n << endl;
}
构建和运行命令是:g++ -std=c++11 ./test.cpp
./a.out
__declval
需要一个参数( int
/long
)? int
) 和模板 2 ( long
) 具有不同的参数类型? 最佳答案
std::declval
实际上是:
template<class T>
typename std::add_rvalue_reference<T>::type declval() noexcept;
哪里 std::add_rvalue_reference<T>
通常是 T&&
,除非在无效的情况下(如 T = void
或 T = int() const
),它只是 T
.主要区别在于函数不能返回数组,但可以返回数组引用,如 U(&&)[]
或 U(&&)[N]
.显式使用
std::add_rvalue_reference
的问题是它实例化了一个模板。在 libstdc++ 实现中,它本身以 ~4 的实例化深度实例化了大约 10 个模板。在通用代码中,std::declval
可以用很多,根据https://llvm.org/bugs/show_bug.cgi?id=27798 ,如果不使用 std::add_rvalue_reference
,编译时间提高了 4% 以上. (libc++ 实现实例化的模板较少,但它仍然有影响)这是通过将“
add_rvalue_reference
”直接内联到declval
来解决的。 .这是使用 SFINAE 完成的。declval<T>
的返回类型是 decltype(__declval<_Tp>(0))
.抬头时__declval
,找到两个函数模板。第一个有返回类型
_Up = T&&
.第二个只有返回类型 T
.第一个需要一个参数
int
,第二个 long
.正在通过0
,这是一个 int
,所以第一个函数是一个更好的匹配并被选中,并且 T&&
被退回。除了,当
T&&
不是有效类型(例如, T = void
),那么当模板参数 _Up
时替换为推导的 T&&
,存在替换失败。所以它不再是函数的候选。这意味着只剩下第二个,而 0
被转换为 long (并且返回类型只是 T
)。在
T
的情况下和 T&&
不能从函数返回(例如, T = int() const
),两个函数都不能被选择,而且 std::declval<T>
函数有替换失败并且不是可行的候选者。这是介绍优化的 libc++ 提交:https://github.com/llvm/llvm-project/commit/ae7619a8a358667ea6ade5050512d0a27c03f432
这是 libstdc++ 提交:https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=ec26ff5a012428ed864b679c7c171e2e7d917f76
他们以前都是
std::add_rvalue_reference<T>::type
关于c++ - 为什么libstdc++-v3中declval的实现看起来那么复杂?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64060929/