1

我正在开发一个使用以下结构的代码库:

啊:

template<int N> void f();
void b();

一个.cpp:

#include "a.h"

template<> void f<1>() {}

int main()
{
    b();
}

b.cpp:

#include "a.h"
void b()
{
    f<1>();
}

该代码似乎可以正确构建和运行。

我的问题是:这是格式正确的,还是某种格式错误的 NDR 恰好起作用?


如果使用clang -Wundefined-func-template (在我的 IDE 的默认 clang-tidy 设置中启用)构建,则会产生警告:

b.cpp:5:2: warning: instantiation of function 'f<1>' required here, but no definition is available [-Wundefined-func-template]
        f<1>();
        ^
./a.h:1:22: note: forward declaration of template entity is here
template<int N> void f();
                     ^
b.cpp:5:2: note: add an explicit instantiation declaration to suppress this warning if 'f<1>' is explicitly instantiated in another translation unit
        f<1>();
        ^

但我不确定是否只是禁用警告,或者进行一些代码更改(除了将显式专业化定义移动到头文件,这对于这个项目来说不是更可取的)。

遵循警告消息中的建议并向头文件(即extern template void f<1>();)添加显式实例化声明会导致错误消息(在显式实例化之前隐式实例化专门化)。

但是,向template<> void f<1>();头文件添加显式特化声明会抑制警告。但我不确定这是否是(a)必要的,和/或(b)推荐的风格。

4

3 回答 3

1

[温度]/7说:

函数模板、类模板的成员函数、变量模板或类模板的静态数据成员应在隐式实例化的每个翻译单元中定义,除非相应的特化在某个翻译单元中显式实例化;不需要诊断。

该标准要求显式实例化,因此显式特化a.cpp不会使程序格式良好。

在CWG2138中提出了一个类似的问题([temp]/7 同等对待函数模板和类模板的成员函数):

目前的规则尚不清楚以下常见做法是否有效:

   // foo.h
   template<typename T> struct X {
    int f(); // never defined
   };
   // foo.cc
   #include "foo.h"
   template<> int X<int>::f() { return 123; }
   // main.cc
   #include "foo.h"
   int main() { return X<int>().f(); }

以 NAD 的形式关闭,原因如下:

正如分析中所述[其中提到了[temp] / 7,除其他外],目的是使示例格式错误,不需要诊断。

因此,答案是:该程序是格式错误的 NDR,这是有意的。

于 2020-04-12T02:15:52.613 回答
1

该程序违反[temp.expl.spec]/6

如果模板、成员模板或类模板的成员是显式特化的,则应在第一次使用该特化之前声明该特化,这将导致发生隐式实例化,在发生这种使用的每个翻译单元中; 不需要诊断。

函数模板在 b.cpp 中f被明确专门化。template<> void f<1>() {}但是在由 b.cpp 和包括 ah 组成的翻译单元中,该语句f<1>();将导致相同特化的隐式实例化f<1>,并且在翻译单元中更早(或任何地方)没有显式特化的声明。

根据标准,显式特化始终与实例化特化不同,因为对于相同的主模板和相同的模板参数,两者永远不会存在。但是该程序可能仍然可以工作,因为许多编译器使用相同的错位链接器名称来进行模板显式特化和实例化特化。

clang 警告可能是因为它是合法的,虽然不寻常,但如果相同的特化在其他地方显式实例化,而不是显式特化,则隐式实例化没有可见定义的函数模板是合法的。因此,它建议进行改进以使法律程序更加清晰。不过,我不确定它是否真的合法。但它建议的显式实例化声明将是一个谎言,因为特化是显式特化的,而不是显式实例化的。

如果您将显式的专业化声明添加到将使用的每个专业化的头文件中,则程序确实有效。

template<int N> void f();
template<> void f<1>();
于 2020-04-11T02:47:12.383 回答
1

[temp.over]/5中有一个示例与您的几乎完全匹配,并且发音良好:

[temp.over]/5 ... [示例:

template<class T> void f(T); // declaration
void g() {
  f("Annemarie"); // call of f<const char*>
}

f即使模板f仅在调用时声明且未定义,调用也是格式良好的。f<const char*>除非某个翻译单元中存在隐式或显式生成的 的特化,否则该程序将是格式错误的。—结束示例]

于 2020-04-11T02:58:08.443 回答