为什么编译器选项会影响模板实现的选择?

时间:2019-07-03 13:33:39

标签: c++ templates g++

根据我是使用-O3进行编译还是未经优化,编译器不会选择相同的function-template-instanciation。使用gcc(Debian 8.3.0-6)8.3.0。

由于疏忽,我在函数模板声明中有一个默认实现:

#pragma once

#include <iostream>

template <int>
void func() { std::cerr << "default impl\n"; } // normally no impl here

及其专长:

#include "func.h"

template <>
void func<1>()
{
    std::cerr << "special 1\n";
}

template <>
void func<2>()
{
    std::cerr << "special 2\n";
}

主要功能。

#include "func.h"

int main(void)
{
    func<1>();
    func<2>();

    return 0;
}

编译并运行g++ -Wall func.cpp main.cpp -o main && ./main可获得:

special 1
special 2

使用优化g++ -O3 -Wall func.cpp main.cpp -o main && ./main可以得出:

default impl
default impl

这是预期的吗?代码是否触发了我不知道的意外行为?

感谢发表Wandbox的评论中的@NathanOliver。进行优化或不进行优化都会显示不同的输出。

2 个答案:

答案 0 :(得分:12)

您的代码格式错误,无需诊断。因此,在不同的优化级别可能会有不同的行为。

  

[temp.expl.spec]

     

6如果是模板,成员模板或类的成员   模板是专门的,那么专门化是   在第一次使用该专业化之前声明   在每个翻译单元中进行隐式实例化   发生了这种使用;无需诊断。如果程序   没有提供明确专业化的定义,并且   要么以一种会导致   进行隐式实例化或成员是虚拟成员   功能,程序格式错误,无需诊断。一个   隐式实例化永远不会为显式生成   已声明但未定义的专业化。

功能模板在一个TU中专用,但是另一个没有可用的专用声明。积极的优化器很可能会选择隐式实例化(可在线使用),而不是在其他地方找到您创建的实例化实例。解决方案是声明标头中存在您的专业化知识。

答案 1 :(得分:3)

由于ODR问题,您的行为不确定。

ODR表示每个符号应该只有一个定义。内联函数和模板函数可以有多个定义,但必须具有相同的实现,一个接一个的标记。如果该规则被破坏,则无需诊断。

在编译示例时,编译器将实例化您的函数。看这个:

template <int>
void func() { std::cerr << "default impl\n"; } // normally no impl here

int main(void)
{
    func<1>();
    func<2>();

    return 0;
}

这是编译器看到的。它看不到其他cpp文件。编译器将实例化模板并为您的函数创建其他定义。

然后您的其他cpp文件将提供另一个不同的定义。

解决方案是在标头中向前声明专业化:

template<> void func<1>();
template<> void func<2>();

这将告诉编译器专业化在其他地方声明,而不是实例化默认专业化。