影响模板重载解析的优化级别

Optimization level affecting template overload resolution

我 运行 关于模板重载解析的有趣行为,它在发布和调试版本之间有所不同。我希望能够理解 compiler/linker 使用的规则,使其以这种方式运行。
我试着挖掘了一下,这就是我想出的:

S.h

#pragma once

#include <cstdint>
#include <iostream>
enum class Enumeration : uint32_t {
    A = 0,
    B
};
struct S {
    template<typename T>
    bool func(Enumeration en) {
        std::cout << "enum nonspec\n";
        return func<T>(static_cast<uint32_t>(en));
    }

    template<typename T>
    bool func(uint32_t en) {
        std::cout << "uint32_t nonspec\n";
        return func<T>(en);
    }
};

S.cpp

#include "S.h"
template<>
bool S::func<float>(uint32_t en) {
    std::cout << "float specialization\n";
    return en;
}

template<>
bool S::func<uint32_t>(uint32_t en) {
    std::cout << "uint32_t specialization\n";
    return en - 1;
}

main.cpp

#include "S.h"

int main() {
    S s;
    Enumeration a = Enumeration::B;
    s.func<float>(a);
    return 0;
}

我已经尝试编译相应的编译单元(S.cpp.o、main.cpp.o 和生成的二进制文件)。 我发现结果完全取决于 main.cpp.o.

编译的优化级别

使用 -O0 输出是
enum nonspec
float specialization.

使用 -O1 输出是
enum nonspec
uint32_t nonspec
uint32_t nonspec
float specialization.

使用 -O2-O3 输出为
enum nonspec
然后无限递归uint32_t nonspec.

我检查了对象文件 -O0
0000000000000000 W bool S::func<float>(Enumeration)
0000000000000000 W bool S::func<float>(unsigned int),
-O1 只有
0000000000000000 W bool S::func<float>(unsigned int),
-O3 没有任何符号。

使用的编译器:gcc 版本 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
使用的链接器:ldd (Ubuntu GLIBC 2.27-3ubuntu1.3) 2.27

我想我明白发生了什么,但我想知道这种行为是否基于任何规则。

C++ 标准要求所有模板特化在使用前声明

[temp.expl.spec]/6:

If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.

If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required.

所以程序格式错误,NDR。编译器不需要发出错误或警告,并且对在这种情况下发生的事情没有限制,因为它永远不会发生。因此,无论它“有效”、“似乎有效”还是“无效”纯属偶然。

可能发生的情况是编译器的优化会内联调用,否则不会,这允许 linker 仍然可以在 link 时间找到专业化。