为什么编译器选项会影响模板实现的选择?
Why do compiler options impact selection of template implementation?
根据我是使用 -O3 编译还是不使用优化,编译器不会 select 相同的函数模板实例化。使用 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
这是预期的吗?代码是否触发了我不知道的意外行为?
感谢@NathanOliver 的评论Wandbox。使用或不使用优化编译显示不同的输出。
由于 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 文件将提供另一个不同的定义。
解决方法是在 header:
中转发专业化声明
template<> void func<1>();
template<> void func<2>();
这将告诉编译器特化是在别处声明的,而不是实例化默认的。
您的代码是 ill-formed,不需要诊断。因此在不同的优化级别下可能会有不同的行为。
[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. An
implicit instantiation is never generated for an explicit
specialization that is declared but not defined.
函数模板专用于一个 TU,但另一个 TU 没有可用的专业化声明。积极的优化器很可能会选择隐式实例化(内联可用)而不是找到您在其他地方创建的实例。解决方案是声明您的专业化存在于 header.
根据我是使用 -O3 编译还是不使用优化,编译器不会 select 相同的函数模板实例化。使用 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
这是预期的吗?代码是否触发了我不知道的意外行为?
感谢@NathanOliver 的评论Wandbox。使用或不使用优化编译显示不同的输出。
由于 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 文件将提供另一个不同的定义。
解决方法是在 header:
中转发专业化声明template<> void func<1>();
template<> void func<2>();
这将告诉编译器特化是在别处声明的,而不是实例化默认的。
您的代码是 ill-formed,不需要诊断。因此在不同的优化级别下可能会有不同的行为。
[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. An implicit instantiation is never generated for an explicit specialization that is declared but not defined.
函数模板专用于一个 TU,但另一个 TU 没有可用的专业化声明。积极的优化器很可能会选择隐式实例化(内联可用)而不是找到您在其他地方创建的实例。解决方案是声明您的专业化存在于 header.