【问题标题】:Is it allowed to use decltype on std::declval<T> (the function itself, not the result of calling it)?是否允许在 std::declval<T> 上使用 decltype (函数本身,而不是调用它的结果)?
【发布时间】:2016-09-15 17:03:35
【问题描述】:

以下代码在 libstdc++ 上触发静态断言:

#include <utility>

using t = decltype(std::declval<const void>);

应该吗?


这个问题的动机:

以下declval 实现proposed by Eric Niebler(这显然是编译时优化)

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));

如果用户可以合法地观察到std::declval&lt;const void&gt; 的类型,那就值得怀疑了。标准中的签名

template <class T>
add_rvalue_reference_t<T> declval() noexcept;

产生const void () 类型(或C++17 中的const void () noexcept),而提议的版本产生void () 类型(或void () noexcept)。

【问题讨论】:

  • 你想用这个来完成什么?
  • @SamVarshavchik,从问题本身来看,问题的原因似乎很清楚......
  • 鉴于您是提出这个问题的人,我对自己的回答的信心为零。但是还是扔出去了。 Niebler 提案中的long 重载是否只是为了处理 cv-void
  • @Barry,除非有另一种类型不能很好地应用引用,否则是的。
  • @ClaasBontus,它类似于常见的 SFINAE 技术,在返回类型中使用decltype 来确定表达式是否有效。在这里,不需要检查表达式,只有 &amp;&amp; 可以应用于类型。请注意,这进一步简化为删除 _Up 并使用 _Tp&amp;&amp; 作为返回类型。如果_Tp&amp;&amp; 是有效类型,则两个重载都将参与重载决议,并且int 胜过long。如果它不是有效类型,则只有第二个重载存在。要了解更多信息,最好的办法是查找表达式 SFINAE 示例。

标签: c++ c++11 language-lawyer


【解决方案1】:

[declval]规定:

如果这个函数被 odr-used (3.2),程序是非良构的。

基本上就是这样。就功能而言,odr-use 表示,来自 [basic.def.odr]:

名称显示为潜在求值表达式的函数是 odr-used 如果它是唯一的查找结果或一组重载函数(3.4、13.3、13.4)的选定成员, 除非它是一个纯虚函数,并且它的名称没有明确限定或表达式形式 指向成员的指针 (5.3.1)。

还有:

一个表达式是可能被计算的,除非它是一个未计算的操作数(第 5 条)或一个子表达式 其中。

还有[dcl.type.simple]:

decltype 说明符的操作数是未计算的操作数(第 5 条)。

所以在decltype(std::declval&lt;const void&gt;) 中,std::declval 没有潜在的评估,因此它不是 odr-used。由于这是declval 程序格式错误的一个标准,我们不满足它,我认为 libstdc++ 发出静态断言是错误的。


虽然我不认为这是 libstc++ 的事情。我认为这更多的是static_asserts 何时被触发的问题。 declval的libstdc++实现是:

template<typename _Tp> 
struct __declval_protector
{    
    static const bool __stop = false;
    static typename add_rvalue_reference<_Tp>::type __delegate();
};   

template<typename _Tp> 
inline typename add_rvalue_reference<_Tp>::type
declval() noexcept
{    
    static_assert(__declval_protector<_Tp>::__stop,
         "declval() must not be used!");
    return __declval_protector<_Tp>::__delegate();
} 

gcc 和 clang 在这种情况下都会触发static_assert(但显然不是decltype(std::declval&lt;const void&gt;()),即使我们在这两种情况下都处于未评估的上下文中。我怀疑这是一个错误,但它可能只是在关于触发static_asserts 的正确行为的标准。

【讨论】:

  • 嗯,我找到了一个不会触发静态断言的新的:using t = decltype(static_cast&lt;const void (*)()&gt;(std::declval&lt;const void&gt;));。我可能会问一个单独的问题 w/r/t 标准中是否有任何内容表明这种演员表不需要工作。
  • @T.C.我希望它们具有同等的有效性,所以我对你没有帮助。但我什至不确定我是否正确。
猜你喜欢
  • 2016-06-02
  • 1970-01-01
  • 1970-01-01
  • 2020-04-03
  • 2015-04-05
  • 2012-10-23
  • 1970-01-01
  • 2023-03-28
  • 2013-01-17
相关资源
最近更新 更多