【问题标题】:where's the point of instantiation of static data member template specialization静态数据成员模板特化的实例化点在哪里
【发布时间】:2020-10-27 08:11:07
【问题描述】:

考虑下面的代码:

#include <iostream>
template<typename T>
struct Test{
    template<typename U>
    static U value;
};
template<typename T>
template<typename U>
U Test<T>::value = U{};
//#1
int main(){
    auto d = Test<int>::value<int>;
}
//#2

标准中的 [temp.point] 部分涵盖了实例化点应放置的大多数情况。但是我认为静态数据成员模板还不清楚,原因是:

temp.point#1

对于函数模板特化、成员函数模板特化或类模板的成员函数或静态数据成员的特化,如果特化是隐式实例化的,因为它是从内部引用的另一个模板特化和引用它的上下文取决于模板参数,特化的实例化点是封闭特化的实例化点。否则,此类特化的实例化点紧跟在引用特化的命名空间范围声明或定义之后。

temp.point#4

对于类模板特化、类成员模板特化或类模板的类成员的特化,如果该特化是隐式实例化的,因为它是从另一个模板特化中引用的,如果引用特化的上下文取决于模板参数,并且如果特化没有在封闭模板的实例化之前被实例化,则实例化点就在封闭模板的实例化点之前。否则,此类特化的实例化点紧接在引用特化的命名空间范围声明或定义之前。

两段都分别涵盖了他们提到的情况,它们是a specialization for static data member of a class templatea class member template specialization,那么,静态数据成员模板的特化可以称为a specialization for static data member of a class templatea class member template specialization吗?我更喜欢将其视为类成员模板特化,我的理由是在第一段中,它提到了成员函数模板特化,这意味着A是否是特化对于X 模板,它会称之为X 模板特化,但这只是我的推论。

在[temp.static]部分,暗示静态数据成员和静态数据成员模板统称为类或类模板的静态数据成员。

temp.static#1

静态数据成员或静态数据成员模板的定义可以在包含静态成员的类模板定义的命名空间范围内提供。

[注意:静态数据成员模板的特化是静态数据成员。成员函数模板的一个特化是成员函数。成员类模板的一个特化是嵌套类。 —— 尾注]

现在,措辞使问题更加不清楚。那么按照上面的规则,Test&lt;int&gt;::value&lt;int&gt;的实例化点是在#2还是#1

如果Test&lt;int&gt;::value&lt;int&gt;的POI在#2,则认为a specialization for static data member of a class template,否则如果在#1,则认为a class member template specialization,不知道是哪个立场是正确的。如果我遗漏了什么,请纠正我。

【问题讨论】:

  • 最后一条语句有什么不清楚的地方?您可以在命名空间范围内提供专业化或作为完全限定名称 ANamespace::Test&lt;T&gt;::value
  • @SwiftFridayPie 请阅读问题。

标签: c++ templates c++17 language-lawyer


【解决方案1】:

Cleiton Santoia Silva 的重点是没有明确的成员模板专业化。 但是,如果您阅读 temp.inst#3:

除非类模板或成员模板的成员被显式实例化或显式特化成员的特化在特化时被隐式实例化在需要成员定义存在的上下文中引用;特别是,静态数据成员的初始化(以及任何相关的副作用)不会发生,除非该静态数据成员本身的使用方式要求该静态数据成员的定义存在。

我们仍然得到一个由 main 中的模板使用触发的特化。 我会说“虚拟专业化”,因为它在代码中是不可见的。这会导致完全就像有明确的专业化一样。

但问题是编译器会在哪里编写这个专业化 - 它在 [temp.point] 中定义。但在哪一段?如果你仔细阅读第4段你可以看到它有一个额外的条件(否则与inner / second if有关):

...如果特化是隐式实例化的,因为它是 从另一个模板特化中引用,如果是特化所在的上下文

类模板“Test”是否引用了成员函数模板?不,它只在 main 中引用。所以,我们不需要看这一段。

但是让我们看看第一段。那里我们有几乎相同的条件,只是没有“if”而是“and”:

对于函数模板特化、成员函数模板特化或类模板的成员函数或静态数据成员的特化,如果特化是隐式实例化的,因为它是从另一个模板特化中引用的并且上下文来自

因为条件不成立,我们需要看一下“else case”:

...否则,这种特化的实例化点紧跟在引用特化的命名空间范围声明或定义之后。

这句话在某种程度上具有误导性。什么是命名空间范围声明(我在标准中没有找到定义)?这里的定义是什么?您的解释是该定义是指模板实例化发生的位置(主要)。但我不这么认为。我认为模板的定义是有意义的。

所以回答你的问题:它应该在 main 之前(在 main 之后会使程序“格式错误”)。但你是对的,应该改进那里的措辞。

顺便说一句: 您可以使用 cppinsights (here) “查看 C++ 编译器内部”。不幸的是,对于隐式全局命名空间范围,它似乎无法正常工作(在这种情况下)。在这里,您可以看到“实际”实例化的特化。


旧答案

克莱顿·桑托亚·席尔瓦是完全正确的。您的源代码仅显示一个静态模板成员声明和一个静态模板成员定义。这不是专业。

here

模板安装发生在这里:

#include <iostream>
template<typename T>
struct Test{
    template<typename U>
    static U value;  //this is a static member template declaration
};
template<typename T>
template<typename U>
U Test<T>::value = U{}; //this is a static member template definition

//this is a partial specialization of a template member variable
template<>
template<typename U>
U Test<char>::value = 42;

//this is a full specialization of a template member variable
template<>
template<>
int Test<int>::value<int> = 22;

int main()
{
    auto d = Test<int> //<- here is Test<int> implicitly instantiated
          ::value<int>; //<- here is the member template ::Test<int>::value<int> implicitly instantiated

    std::cout << Test<char>::value<int> << "\n"; //this prints 42
    std::cout << Test<int>::value<int> << "\n"; //this prints 22
    std::cout << Test<bool>::value<bool> << "\n"; //this prints 0
}

如果你写这个 - 你的程序会变得不正确:

#include <iostream>
template<typename T>
struct Test{
    template<typename U>
    static U value;  //this is a static member template declaration
};
template<typename T>
template<typename U>
U Test<T>::value = U{}; //this is a static member template definition


int main()
{
    auto d = Test<int> //<- here is Test<int> instantiated
          ::value<int>; //<- here is the member template instantiated

    std::cout << Test<char>::value<int> << "\n"; //this prints 42
    std::cout << Test<int>::value<int> << "\n"; //this prints 22
    std::cout << Test<bool>::value<bool> << "\n"; //this prints 0
}

//this is a partial specialization of a template member variable
template<>
template<typename U>
U Test<char>::value = 42;

//this is a full specialization of a template member variable
template<>
template<>
int Test<int>::value<int> = 22;

不过这样就好了。非内联静态成员定义必须在 cpp 文件中定义,以免违反 ODR。 但与所有模板一样 - 每个模板定义都必须在所有翻译单元中可用。这就是为什么这样的定义通常在标题中定义的原因:

#include <iostream>
template<typename T>
struct Test{
    template<typename U>
    static U value;  //this is a static member template declaration
};

//this is a partial specialization of a template member variable
template<>
template<typename U>
U Test<char>::value = 42;

//this is a full specialization of a template member variable
template<>
template<>
int Test<int>::value<int> = 22;

int main()
{
    auto d = Test<int> //<- here is Test<int> implicitly instantiated
          ::value<int>; //<- here is the member template ::Test<int>::value<int> implicitly instantiated

    std::cout << Test<char>::value<int> << "\n"; //this prints 42
    std::cout << Test<int>::value<int> << "\n"; //this prints 22
    std::cout << Test<bool>::value<bool> << "\n"; //this prints 0
}

template<typename T>
template<typename U>
U Test<T>::value = U{}; //this is a static member template definition

【讨论】:

  • 不,模板 ID Test&lt;int&gt;::value&lt;int&gt; 指的是需要隐式实例化的特化。
  • 并且类模板定义中的声明是静态成员template声明,外部是静态成员template定义。
  • 它是一个成员模板 - 这是真的。但这并不特别。静态成员模板必须像“普通”静态成员一样定义。这不是专业。
  • 其实是我。这个答案回答了这个问题。问题是“Test&lt;int&gt;::value&lt;int&gt; 在哪里实例化?”这个答案说它是在 in main 中实例化的,但事实并非如此,因为您自己无法将其专门用于 main。这就是请求(隐式)特化的地方,但根据标准,实际实例化必须发生在之前或之后。问题只是“哪个?”。
  • @BerndBaumanns 您可以在函数中请求模板特化(如您所说,通过命名)。正如问题中的引号所证明的,实例化点(控制实例化定义可以看到的名称等)位于main 之外的某个地方。问题到底出在哪里。
【解决方案2】:

您可能会混淆实例化/专业化

template<typename T>
template<typename U>
U Test< T >::value = 88;  // <- this is not specialization 

template<>
template<>
int Test< int >::value<int> = 98;  // <- this is specialization

运行此代码https://godbolt.org/z/h434eG,查看输出中数字的顺序,然后取消注释具有特化的块并再次运行。

【讨论】:

  • 我对@9​​87654324@ 并不感到困惑,您的代码似乎无法证明Test&lt;int&gt;::value&lt; Debug &gt; 的POI 位于#1。如果是#2,结果还是和你的代码一样
  • 代码中的注释块被称为显式特化,如果取消注释,它将阻止Test&lt;int&gt;::value&lt; Debug &gt; 的隐式实例化,请参阅here,因此,您的代码实际上什么也证明不了。
猜你喜欢
  • 1970-01-01
  • 2011-04-11
  • 1970-01-01
  • 2017-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多