【问题标题】:What is decltype with two arguments?带两个参数的 decltype 是什么?
【发布时间】:2013-04-09 07:10:29
【问题描述】:

编辑,以避免混淆:decltype 接受两个参数。查看答案。

以下两个结构可用于在编译时检查类型 T 上的成员函数是否存在:

// Non-templated helper struct:
struct _test_has_foo {
    template<class T>
    static auto test(T* p) -> decltype(p->foo(), std::true_type());

    template<class>
    static auto test(...) -> std::false_type;
};

// Templated actual struct:
template<class T>
struct has_foo : decltype(_test_has_foo::test<T>(0))
{};

我认为这个想法是在检查成员函数是否存在时使用 SFINAE,所以如果p-&gt;foo() 无效,则只定义返回std::false_type 的省略号版本test。否则为T* 定义第一个方法并将返回std::true_type。实际的“切换”发生在第二个类中,它继承自 test 返回的类型。与 is_same 之类的不同方法相比,这似乎很聪明且“轻量级”。

带有两个参数的decltype 最初让我感到惊讶,因为我认为它只是获取表达式的类型。当我看到上面的代码时,我认为它类似于“尝试编译表达式并始终返回第二个的类型。如果表达式编译失败则失败”(所以隐藏这个专业化;SFINAE)。

但是:

然后我想我可以使用这种方法来编写任何“有效表达式”检查器,只要它依赖于某种类型T。示例:

...
    template<class T>
    static auto test(T* p) -> decltype(bar(*p), std::true_type());
...

http://ideone.com/dJkLPF

我认为,当且仅当bar 定义为接受T 作为第一个参数(或者如果T 是可转换的,等等),这将返回std::true_type,即:如果bar(*p) 是在p 定义为T* 类型的上下文中编写的,则它会编译。

然而,上面的修改总是评估为std::false_type为什么会这样?我不想用一些复杂的不同代码来修复它。我只是想知道为什么它不能像我预期的那样工作。显然,带有两个参数的 decltype 的工作方式与我想象的不同。我找不到任何文件;处处只能用一种表达方式来解释。

【问题讨论】:

  • @Praetorian:天哪。我对这个问题感到困惑,现在有了你的评论,我知道为什么了。这太可怕了。整洁。
  • @Praetorian 这太容易了。如果您只使用其他人编写的代码,就会发生这种情况...... ;)

标签: c++ c++11 sfinae typetraits decltype


【解决方案1】:

这是一个逗号分隔的表达式列表,类型与列表中最后一个表达式的类型相同。它通常用于验证 first 表达式是否有效(可编译,想想 SFINAE),第二个用于指定 decltype 应该在第一个表达式有效的情况下返回。

【讨论】:

  • 好的,但是,如果bar 接受T,为什么它不“接受”bar(*p)
  • @leemes 在您链接的示例中bar 不接受所有Ts,它只接受std::ostream。因此,如果 *p 在第二次调用中产生 std::istreambar(*p) 将失败。
  • @leemes 那是因为std::ostream 是不可复制的。也许这个例子应该有int bar(const std::ostream&amp;){return 0;}?如果你把它改成这个,它就可以了。
  • 好的,我现在有一个不同的例子,它有问题(我的实际问题):我想测试QVariant::fromValue(t) 的有效性,更准确地说是它是否可以编译(不仅仅是它的签名)。在这里,它有所不同,因为它是一个模板化函数,它使用类型来查询更多信息,这可能会失败(在函数本身内!)。 decltype 似乎对此没有任何问题。在这种情况下,它总是将检查器定义为真。这种方法似乎不可能编写“表达式将编译”检查,而只是“具有正确的签名/类型是有效的”。
  • @leemes 一个 XY 问题 :) 我建议您向 SSCCE 提出一个关于您的真正问题的新问题,因为我不确定评论是否足以解释它。
【解决方案2】:

decltype 不接受两个参数。简单地说,它可以有一个表达式作为它的参数,而逗号运算符是创建表达式的一种方式。根据第 5.18/1 段:

[...] 一对用逗号分隔的表达式从左到右计算;左表达式是丢弃值 表达式(第 5 条)。与左表达式相关的每个值计算和副作用 在与正确表达式相关的每个值计算和副作用之前排序。 类型 结果的和值是右操作数的类型和值;结果是相同的值类别 作为它的右操作数,并且如果它的右操作数是一个左值和一个位域,则它是一个位域。如果正确的值 操作数是临时的(12.2),结果是临时的。

因此:

static_assert(std::is_same<decltype(42, 3.14), double>::value, "Will not fire");

【讨论】:

  • 好的,这就解释了 decltype 的作用。但是你能解释一下为什么我的第二个检查器总是返回std::false_type,即 decltype 的东西无法编译(至少这是我解释结果的方式,如我的 ideone 代码中所见)?
  • @leemes:抱歉,你在比萨人到来前半秒放弃了你的评论 :) 基本上,Daniel Frey 在他对他的回答的最后评论中解释了这一点
  • @AndyProwl 你能帮我理解你的static_assert 代码吗?逗号运算符是否会导致始终返回 "Will not fire",而与以下结果无关:std::is_same&lt;decltype(42, 3.14), double&gt;::value
  • @JonathanMee No. static_assert 接受两个参数。
  • @JonathanMee:那不是逗号运算符。 static_assert 接受两个参数:一个布尔表达式和一个字符串文字。如果布尔表达式的计算结果为 false,编译时断言会触发,并且字符串文字的内容会显示在错误消息中。否则,不会触发编译时失败。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-18
  • 2013-02-09
相关资源
最近更新 更多