根据我是使用-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。进行优化或不进行优化都会显示不同的输出。
答案 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>();
这将告诉编译器专业化在其他地方声明,而不是实例化默认专业化。