Structs with the same name/namespace but different

2019-04-15 18:12发布

I am seeing some strange behavior which I have described in this post vector of structs inline initialization. braced-init-list

I am hoping to eliminate one possible source of the undefined behavior. I have the following two classes A and B which both forward declare struct Foo but the cpp file defines the actual struct as below.

// 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;
};

Notice, that the struct Foo has different members in cpp files A.cpp, B.cpp. The classes A and B never expose the member fooList. Is it ever possible that since the forward declaration is exactly the same, in files A.h and B.h for the struct Foo, the generated code could be using one or the other. This could explain the issue I was seeing in the linked problem.

In other words, while using the braced-init-list for the struct Foo invoked in B.cpp is it guaranteed that Foo defined in B.cpp would be used or it is equally likely that Foo defined in A.cpp could also be used?

Even as I am writing this, I immediately realize that this implementation is a bad practice as Foo itself is internal to the classes A and B and should really have been declared in the private section of the class itself.

标签: c++ c++11 struct
1条回答
等我变得足够好
2楼-- · 2019-04-15 18:27

This violates the ODR (one definition rule).

The program is ill-formed, no diagnostic required.

Absolutely any behavior is permitted by the C++ standard if you do this. It could "work", it could pick one and discard the other, it could work until you relink, it could format your hard drive.

I have done this in a real live project; we had a matrix header where you could define tokens before including it if it would support float or double.

This "worked" while we never used both versions in the same DLL. Then we used both versions.

The compiler would pick one size or the other for the structure based on one set of coincidences, and would pick one constructor or other based off a slightly different set of coincidences. We got memory corruption galore. But only sometimes on some builds.

We quickly "fixed" it by wrapping the code in a namespace whose name included the scalar type, then using brought them into the exterior namespace.

查看更多
登录 后发表回答