我正在阅读Leor Zolman先生提出的一些名为An Overview of C++11 and C++14的幻灯片。在第35页,他介绍了一种使用decltype
进行总和操作的方法。
struct Sum {
template <typename T>
static T sum(T n) {
return n;
}
template <typename T, typename... Args>
/// static T sum(T n, Args... rest) {
static auto sum(T n, Args... rest) -> decltype(n + sum(rest...)) {
return n + sum(rest...);
}
};
当使用Sum::sum(1, 2.3, 4, 5);
clang-3.6(来自svn)的这个片段无法用-std=c++11
/ -std=c++1y
编译时,但gcc-4.9成功。当然没有返回类型的类型推导都是编译,但是涉及类型转换并且无法获得预期的结果。
这是否表示一个clang bug,或者是因为gcc扩展(关于c ++ 11或c ++ 14)?
答案 0 :(得分:10)
Clang的行为是正确的。这是一个GCC错误(并且演示文稿中的声明也不正确)。 §3.3.2[basic.scope.pdecl] / p1,6:
1名称的声明紧跟在其之后 完整的声明者(第8条)和初始化者之前(如果有的话), 除非如下所述。
6在声明类成员之后,成员名称可以 被提升到同班的范围内。
§3.3.7[basic.scope.class] / p1说
以下规则描述了在类中声明的名称范围。
1)类中声明的名称的潜在范围不仅包括在内 名称的声明点后的声明区域, 而且所有函数体,默认参数, exception-specifications 和 brace-or-equal-initializers 该类中的非静态数据成员(包括嵌套中的这些内容) 类)。
trailing-return-types 不在该列表中。
尾随返回类型是声明符的一部分(§8[dcl.decl] / p4):
declarator:
ptr-declarator
noptr-declarator parameters-and-qualifiers trailing-return-type
因此sum
的可变版本不在其自己的 trailing-return-type 范围内,并且无法通过名称查找找到。
在C ++ 14中,只需使用实际的返回类型推导(并省略尾随返回类型)。在C ++ 11中,您可以使用类模板和简单转发的函数模板:
template<class T, class... Args>
struct Sum {
static auto sum(T n, Args... rest) -> decltype(n + Sum<Args...>::sum(rest...)) {
return n + Sum<Args...>::sum(rest...);
}
};
template<class T>
struct Sum<T>{
static T sum(T n) { return n; }
};
template<class T, class... Args>
auto sum(T n, Args... rest) -> decltype(Sum<T, Args...>::sum(n, rest...)){
return Sum<T, Args...>::sum(n, rest...);
}