【问题标题】:The problem of libstdc++‘s implementation of std::declvallibstdc++实现std::declval的问题
【发布时间】:2021-07-30 11:46:50
【问题描述】:

与只声明std::declval的函数签名的libc++MSVC-STL不同,libstdc++std::declval定义函数体,并在内部使用static_assert以确保它必须在未评估的上下文中使用:

template<typename _Tp, typename _Up = _Tp&&>
_Up 
__declval(int);

template<typename _Tp>
_Tp 
__declval(long);

template<typename _Tp>
auto 
declval() noexcept -> decltype(__declval<_Tp>(0));

template<typename _Tp>
struct __declval_protector {
  static const bool __stop = false;
};

template<typename _Tp>
auto 
declval() noexcept -> decltype(__declval<_Tp>(0)) {
  static_assert(__declval_protector<_Tp>::__stop,
    "declval() must not be used!");
  return __declval<_Tp>(0);
}

当我们尝试评估std::declval的返回值时,这个static_assert会被触发:

auto x = std::declval<int>(); // static assertion failed: declval() must not be used!

但是我发现在这个实现中,下面的代码也会因为static_assert失败(godbolt)而被拒绝:

#include <type_traits>

template<class T>
auto type() { return std::declval<T>(); }

using T = decltype(type<int>());

但似乎std::declval 仍在未评估的上下文中使用,因为我们尚未实际评估它。

上面的代码格式正确吗?如果是这样,它是一个库实现错误吗?标准是如何规定的?

【问题讨论】:

  • 最后一个神螺栓链接是错误的例子
  • std::declval 只允许在未计算的表达式中使用。 return std::declval&lt;T&gt;(); 不是未评估的表达式。我认为 clang 拒绝此代码是正确的。
  • 查看此处了解未评估的上下文是什么:stackoverflow.com/questions/35088599/…。您不调用的函数不是未评估的上下文
  • 请注意,libstdc++ 对std::declval 的实现会使程序格式错误,在 std 库的实现之外不需要诊断。但是其他的可能也是如此(所有专业化必须具有合法实例化的要求;__declval_protector 试图解决这个问题,但偷偷摸摸并不能使它形成良好的形式)。这实际上不是问题,因为标准对 std 头文件的内容没有任何限制。
  • 该问题已针对 MSVC:github.com/microsoft/STL/blob/… 修复。该修复程序将在 VS 2022 中提供

标签: c++ metaprogramming c++20 typetraits


【解决方案1】:

这个例子是错误的,因为std::declval 的限制实际上是在declval 被命名的表达式上,而不是抽象虚拟机是否真的会评估它的调用。

std::declval 的要求是[declval].2

任务:此功能不用于 odr ([basic.def.odr])。

“授权”是指 ([structure.specifications]/(3.2))

要求:如果不满足,会导致程序格式错误的条件。

[basic.def.odr]/7:

如果函数由潜在求值的表达式或转换命名,则该函数是 odr-used。

[basic.def.odr]/2:

表达式或转换是可能被评估的,除非它是未评估的操作数 ([expr.prop])、其子表达式,或者在这种上下文中的初始化或转换序列中的转换。

请注意,最后一行中的表达式type&lt;int&gt;() 是一个未计算的操作数。但是type 的实例化定义中的不同表达式std::declval&lt;T&gt;() 可能会被评估。忽略这一点并不重要,它实际上不会被评估。

所以实际上 libc++ 和 MSVC-STL 实现是不正确的,因为除非另有说明,否则应该诊断格式错误的程序。

【讨论】:

    猜你喜欢
    • 2012-11-07
    • 2021-01-11
    • 2013-06-05
    • 1970-01-01
    • 1970-01-01
    • 2018-03-02
    • 2019-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多