我看到我在这篇帖子vector of structs inline initialization. braced-init-list
中描述了一些奇怪的行为我希望消除这种不确定行为的一种可能来源。我有以下两个类A
和B
都向前声明struct Foo
,但是cpp文件定义了如下的实际结构。
// This is A.h
namespace app {
struct Foo;
class A {
private:
std::vector<Foo> fooList;
};
}
// This is A.cpp
struct app::Foo {
std::string first;
std::string second;
unsigned flag;
};
// This is B.h
namespace app {
struct Foo;
class B {
private:
std::vector<Foo> fooList;
};
}
// This is B.cpp
struct app::Foo {
std::string first;
std::string second;
bool flag;
float value;
};
请注意,结构Foo
在cpp文件A.cpp
,B.cpp
中具有不同的成员。类A
和B
从不公开成员fooList
。由于前向声明完全相同,因此是否有可能在结构A.h
的文件B.h
和Foo
中,所生成的代码可以使用其中之一。这可以解释我在链接问题中看到的问题。
换句话说,当对在Foo
中调用的结构B.cpp
使用braced-init-list时,可以保证将使用Foo
中定义的B.cpp
或同样可能也使用Foo
中定义的A.cpp
吗?
即使在写这篇文章时,我也立即意识到这种实现是一种不好的做法,因为Foo
本身是类A
和B
的内部,并且实际上应该在类本身的私有部分。
答案 0 :(得分:9)
这违反了ODR(一种定义规则)。
程序格式错误,无需诊断。
如果这样做,C ++标准绝对允许任何行为。它可以“工作”,可以选择其中一个然后丢弃另一个,可以工作直到重新链接,然后可以格式化硬盘。
我已经在一个真实的项目中做到了这一点;我们有一个矩阵标头,您可以在其中包含令牌(如果它支持float或double的话)之前定义令牌。
这是“有效的”,而我们从未在同一个DLL中使用两个版本。然后我们使用了两个版本。
编译器将基于一组巧合为结构选择一种或另一种大小,并根据一组稍有不同的巧合选择一种构造函数或另一种构造函数。我们有大量的内存损坏问题。但仅在某些版本上有时如此。
我们通过将代码包装在一个名称空间中来对其代码进行“修复”,该名称空间中包含标量类型,然后using
将其引入外部名称空间。