代码之家  ›  专栏  ›  技术社区  ›  javidcf

为什么显式模板实例化不能破坏ODR?

  •  3
  • javidcf  · 技术社区  · 7 年前

    this answer .

    template <int Num> int getNum() { return Num; }
    template int getNum<0>();
    template int getNum<0>();  // error: duplicate explicit instantiation of 'getNum<0>'
    int main() { getNum<0>(); return 0; }
    

    我明白这一点,我已经尝试过两次对同一个显式模板进行实例化。然而,事实证明,将其划分为不同的单元,它会编译:

    // decl.h
    template <int Num> int getNum() { return Num; }
    
    // a.cc
    #include <decl.h>
    template int getNum<0>();
    
    // b.cc
    #include <decl.h>
    template int getNum<0>();
    int main() { getNum<0>(); return 0; }
    

    我没想到会这样。我假设具有相同参数的多个显式模板实例化会破坏ODR,但事实似乎并非如此。然而,这确实失败了:

    // decl.h
    template <int Num> int getNum();
    
    // a.cc
    #include "decl.h"
    template <> int getNum<0>() { return 0; }
    
    // b.cc
    #include "decl.h"
    template <> int getNum<0>() { return 0; }
    int main() { getNum<0>(); return 0; }
    

    用户 Oliv 帮我指了指 this relevant paragraph in the standard

    编辑:

    再举一个例子,这里有一个程序被分为两个单元,可以正确编译,但却产生了令人惊讶的结果:

    // a.cc
    template <int Num> int getNum() { return Num + 1; }
    template int getNum<0>();
    
    // b.cc
    #include <iostream>
    template <int Num> int getNum() { return Num; }
    template int getNum<0>();
    int main() { std::cout << getNum<0>() << std::endl; return 0; }
    

    输出:

    1
    

    0 . 我知道拥有两个定义不同的模板不是一个常见的用例,但我认为ODR的实施恰恰是为了避免这种问题。

    2 回复  |  直到 7 年前
        1
  •  4
  •   Oliv    7 年前

    尤里卡!我终于谈到了相关的段落,[临时规范]/5

    对于给定的模板和一组给定的模板参数,

    • (5.2) 按照[basic.def.odr]中的规定,显式专门化应在程序中最多定义一次,并且

    • (5.3)

    所以显式模板实例化定义(不是隐式实例化)会导致ODR冲突,不需要诊断(至少gcc和clang-ld工具链不会产生诊断)

        2
  •  0
  •   Community Mohan Dere    6 年前

    下面解释第一个和第三个案例,以及为什么它们违反了NDR的ODR [temp.spec]/5

    对于给定的模板和一组给定的模板参数,

    • (5.2)明确的专业化在一个程序中最多只能定义一次(根据6.2),[…]

    当这些专门化在所有实例化点的意义相同时,违反ODR。

    [temp.point]/6

    显式实例化定义是显式实例化指定的一个或多个专门化的实例化点。

    [temp.point]/8

    […]如果两个不同的实例化点根据一个定义规则(6.2)赋予一个模板专门化不同的含义,则程序的格式不正确,不需要诊断。

    第二种情况并不违反ODR,因为这些TU中实例化的含义是相同的。

    // decl.h
    template <int Num> int getNum() { return Num; }
    
    // a.cc
    #include <decl.h>
    template int getNum<0>();
    
    // b.cc
    #include <decl.h>
    template int getNum<0>();
    int main() { getNum<0>(); return 0; }
    

    但是最后一个肯定是无效的(违反ODR NDR),因为即使函数模板有相同的签名,它们的实例化也会有不同的含义。你不能传递你得到的结果,标准不能保证这些违规行为发生时的行为。

    // a.cc
    template <int Num> int getNum() { return Num + 1; }
    template int getNum<0>();
    
    // b.cc
    #include <iostream>
    template <int Num> int getNum() { return Num; }
    template int getNum<0>();
    int main() { std::cout << getNum<0>() << std::endl; return 0; }
    
    推荐文章