让我们考虑一些合成但富有表现力的例子。假设我们有Header.h:
Header1.h
#include <iostream>
// Define generic version
template<typename T>
inline void Foo()
{
std::cout << "Generic\n";
}
Header2.h
void Function1();
Header3.h
void Function2();
Source1.cpp
#include "Header1.h"
#include "Header3.h"
// Define specialization 1
template<>
inline void Foo<int>()
{
std::cout << "Specialization 1\n";
}
void Function1()
{
Foo<int>();
}
后来我或其他人在另一个源文件中定义了类似的转换。Source2.cpp
#include "Header1.h"
// Define specialization 2
template<>
inline void Foo<int>()
{
std::cout << "Specialization 2\n";
}
void Function2()
{
Foo<int>();
}
main.cpp
#include "Header2.h"
#include "Header3.h"
int main()
{
Function1();
Function2();
}
问题是什么将打印Function1()和Function2()?答案是未定义的行为。
我希望在输出中看到:专业化1专业化2
但我明白了:专业化2专业化2
为什么C++编译器对ODR冲突保持沉默?我宁愿在这种情况下编译失败。
我只找到一个解决方法:在未命名的命名空间中定义模板函数。
2条答案
按热度按时间0lvr5msh1#
编译器是静默的,因为它不是[basic.def.odr/4]发出任何东西所必需的:
每一个程序都必须包含一个非内联函数或变量的定义,这些函数或变量在程序中被丢弃的语句之外被odr使用;**不需要诊断。**定义可以显式出现在程序中,可以在标准或用户定义库中找到,或者(在适当的情况下)隐式定义(参见[class.ctor],[class.dtor]和[class.copy])。内联函数或变量应在每个转换单元中定义,在每个转换单元中,内联函数或变量在被丢弃的语句之外被odr使用。
nszi6y052#
在极少数情况下,违反网上解决可能是有益的。
例如,你可以使用
std::aligned_storage<MyPimplType_sizeof, MyPimplType_alignof>
来代替std::unique_ptr<MyPimplType>
,并在MyPimplType
类的构造函数/析构函数中测试真实的的sizeof和对齐。这称为aligned storage pimpl
或类似的东西。当你想用impl-by-pointer(通过智能指针指向impl)替换impl-by-value(对齐存储而不是impl)时,这很有用。接下来,可以创建一种新的对齐存储类型,它可以在构造函数/析构函数中自动测试sizeof和对齐:
private/my_aligned_storage_by_decl.hpp或public/my_aligned_storage_by_decl.hpp
private/my_aligned_storage_by_impl.hpp
这只能通过ODR违规来实现 * 并且如果公共和私有头不能合并为单个头,其中公共和私有头具有相同
my_aligned_storage_by
类的 *2个不同定义 *。https://github.com/andry81/tacklelib/tree/HEAD/include/tacklelib/tackle/aligned_storage/
include/myheader.hpp
src/_impl/myheader_this.hpp
src/MyClass.cpp
以下是这种方法的一些主要缺点:
1.你必须将包含
my_aligned_storage
作为成员的类的头文件也拆分为公共/私有头文件,并将公共头文件留给SDK,但将私有头文件包含在cpp文件中而不是公共头文件中。1.您已经显式地控制了这些头的包含顺序,因为可以静默地包含私有头而不是公共头(但反之亦然)。
1.只有当类型变得完全完整时,才必须包含sizeof/alignment测试Assert的实现,这有时并不总是可能的。
1.你已经显式地指定了sizeof和alignment,它们在不同的上下文中可以是不同的,例如debug/release、windows/linux、msvc/gcc等等。
(*)如果
my_aligned_storage
的public和private header不能合并为一个public声明header。这些缺点可以避免或忽略的情况下,对齐的用户类真的不是很大,经常构造/分配/复制,像一个内置类型。