4

我遇到了当前 C++ 编译器(clang/gcc)确定名称是否依赖的方式不一致。在下面的示例中,A::f是依赖但::f不是,使用后者时会导致错误。

template<typename>
struct B
{
    typedef int Type;
};

template<typename U>
static U f(U u);

template<typename T>
struct A
{
    template<typename U>
    static U f(U u);

    typename B<decltype(f(0))>::Type m1; // typename required
    B<decltype(::f(0))>::Type m2; // typename not required
};

不一致的部分是声明A::f不依赖于的模板参数A,这意味着似乎没有必要将其视为依赖名称。

C++11 标准中的以下措辞似乎涵盖了此行为:

[temp.dep.expr]/3

一个 id 表达式是依赖于类型的,如果它包含

  • 一个标识符,通过名称查找与一个或多个用依赖类型声明的声明关联

[温度.dep.type]/3

一个类型是依赖的,如果它是

  • 从任何依赖类型构造的复合类型

的声明::f显然不依赖,因为它的类型只依赖于它自己的模板参数。为什么要A::f区别对待?

4

1 回答 1

3

我认为基于标准,f实际上是不依赖的。

14.6.2.2 类型相关的表达式 [temp.dep.expr]

3 一个id 表达式是依赖于类型的,如果它包含

  • 一个标识符,通过名称查找与使用依赖类型声明的一个或多个声明相关联,

这同样适用于全局模板函数,就像它适用于成员模板函数一样:一点也不。的返回类型U取决于模板函数的定义,但对于调用者来说,函数类型f<int>已经从 转换U(U)int(int)。无论如何,它不能解释为什么编译器会以不同的方式处理这两种情况,也不能解释为什么非模板成员函数也被视为依赖。

  • 一个依赖的模板ID
  • 指定依赖类型的转换函数 ID,或

这些不适用。在template-id中没有<or>必须始终存在,并且没有调用转换函数。

  • 命名未知专业化成员的嵌套名称说明符限定 ID ;

见下文。

T或者,如果它为某些T(14.5.1.3)命名了具有“未知边界数组”类型的当前实例化的静态数据成员。

这也不适用:不涉及数组。

所以这取决于是否f是未知专业的成员。但它不是:

14.6.2.1 依赖类型 [temp.dep.type]

5 一个名字是一个未知专业的成员,如果它是

  • 一个合格的ID,其中[...]。
  • 一个合格的ID,其中[...]。
  • 一个id 表达式,表示类成员访问表达式 (5.2.5) 中的成员,其中 [...].

这些不能应用:f既不是限定的,也不是类成员访问表达式的一部分。

由于唯一f可以依赖的方法是如果它是未知专业化的成员,并且它不是未知专业化的成员,则f一定不能依赖。

至于为什么编译器仍然将其视为依赖,我没有答案。我的答案的某些部分是错误的,编译器有错误,或者编译器遵循不同版本的 C++ 标准。使用一个不管名称是否依赖都可以工作的示例进行测试显示了编译器处理的一些变化:

#include <cstdio>

void f(const char *s, ...) { std::printf("%s: non-dependent\n", s); }

struct S1 { };

template <typename T>
struct S2 {
  static S1 a;
  static S1 b() { return {}; }
  template <typename U>
  static U c() { return {}; }
  static void z() {
    f("S1()", S1()); // sanity check: clearly non-dependent
    f("T()", T()); // sanity check: clearly dependent
    f("a", a); // compiler agreement: non-dependent
    f("b()", b()); // compiler disagreement: dependent according to GCC 4.8, non-dependent according to clang
    f("c<T>()", c<T>()); // sanity check: clearly dependent
    f("c<S1>()", c<S1>()); // compiler agreement: dependent
    f("decltype(b())()", decltype(b())()); // compiler agreement: dependent
  }
};

void f(const char *s, S1) { std::printf("%s: dependent\n", s); }

// Just to show it's possible to specialize the members
// without specializing the full template.
template <>
S1 S2<S1>::b() { return {}; }
template <>
template <>
S1 S2<S1>::c<S1>() { return {}; }

int main() {
  S2<S1>::z();
}

b()clang 处理,decltype(b())()和的这种差异c<S1>()对我来说尤其令人不安。这没有任何意义。他们显然都同样依赖。我可以从实现的角度理解,必须注意不要为成员函数生成代码,因为可能有S2<S1>::bor的特殊化S2<S1>::c<S1>,但这适用于所有,并且对返回类型没有影响。

于 2014-12-27T11:39:04.267 回答