50

编辑,以避免混淆: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->foo()无效,则只定义test返回 的省略号版本。std::false_type否则为第一个方法定义T*并将返回std::true_type。实际的“切换”发生在第二个类中,它继承自test. 与使用类似方法的不同方法相比,这似乎很聪明且“轻量级” is_same

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

但:

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

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

http://ideone.com/dJkLPF

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

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

4

2 回答 2

47

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

于 2013-04-16T18:36:40.540 回答
18

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

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

所以:

static_assert(std::is_same<decltype(42, 3.14), double>::value, "Will not fire");
于 2013-04-16T18:36:58.377 回答