模板特化/初始化和命名空间?

时间:2014-01-10 10:07:27

标签: c++ template-specialization static-initialization fully-qualified-naming

关于模板特化和命名空间限定的C ++规则是什么?我有一些代码可以归结为以下内容,它让我意识到我不理解C ++关于模板特化初始化的规则。对我来说,g::F<>的特殊化甚至在h内被允许似乎很奇怪,但鉴于此,我不知道为什么事情会像他们那样行事。

namespace g {

  struct N {
    N(char c):c_(c){}
    char c_;
  };

  template <typename V>
  struct F {
    static N n_s; // <-- want to initialize these for specializations
  };

  namespace h {
    struct X { static constexpr char k{'x'}; };

    template <> N F<char>::n_s{h::X::k};  // OK
    template <> N F<int>::n_s{X::k};      // fails on "‘X’ not declared"
  }
} // namespace g

// Seems weirdest to me. N and F need full qualifications but X doesn't.
template <> g::N g::F<float>::n_s{h::X::k}; // OK also!

模板静态成员初始值设定项推断g命名空间的最后一个实例化/初始化使得模板看起来好像初始化程序的行为就好像它位于代码中的相同位置一样模板定义本身。

规范中规定这种行为的规则是什么? (请注意,这是在gcc 4.8.1上测试的,到目前为止看起来有点像bug ...)

2 个答案:

答案 0 :(得分:2)

这里的关键混淆不是关于专业化,而是关于资格。让我们看看最后一个特化(在全局命名空间中)来说明这一点:

template <> g::N g::F<float>::n_s{h::X::k};

当行开始时,您处于全局命名空间中。因此,g::N必须是合格的,g::F<float>也是如此。

然而,当你超越你专攻的东西时(即n_s之后),你现在处于你专攻的范围内,即在g::F内。所有进一步查询都在此范围内完成,因此x必须限定为h::X

也就是说,虽然允许在包含原始命名空间的命名空间中对事物进行特化处理,但在嵌套命名空间(在您的情况下为h)中进行特殊处理对我来说看起来很奇怪,但标准在这里有点模棱两可,如它在14.7.3 / 2中说:“应在包含专用模板的命名空间中声明显式专门化。”

全局命名空间包含F,所以这很好,g也是如此。 h并未包含F,但h位于g之内,因此专业化也在g范围内,从技术上讲,这很好。但是,通过这种推理,您可以在任何地方专门化一个模板,因为您始终位于全局命名空间内,该命名空间包含所有内容。所以我很确定GCC在这里的行为过于宽松,而且是一个错误。您也应该尝试使用其他编译器,然后提交错误,或者可能是针对标准的缺陷报告。

答案 1 :(得分:-1)

在:

template <> N F<int>::n_s{X::k};      // fails on "‘X’ not declared"

失败是因为与F<int>::n_s关联的命名空间为g,而X中声明了g::h。这就是你需要像h::F<int>::n_s一样拼写出来的原因。