Search code examples
c++structtypedefusingunnamed-class

Error defining an unnamed structure in c++


I'm having a problem with my code, even my professor has no idea why this is the case.

He wants me to define the structure with using Persontype= struct{}; and not a normal definition of a structure. I don't understand why or what is the difference.

he also don't want us to include the .cpp file in main.

Normal definition of a struct works fine but not with this.

the error

In file included from ...\LABOR1TEST\main.cpp:3:
...\LABOR1TEST\test.hpp:12:6: warning: 'void test(const std::vector<<unnamed struct> >&)' used but never defined
 void test(std::vector<PersonType> const &_Personen);
      ^~~~
[100%] Linking CXX executable LABOR1TEST.exe
CMakeFiles\LABOR1TEST.dir/objects.a(main.cpp.obj): In function `main':
.../LABOR1TEST/main.cpp:12: undefined reference to `test(std::vector<._56, std::allocator<._56> > const&)'
collect2.exe: error: ld returned 1 exit status
mingw32-make.exe[3]: *** [CMakeFiles\LABOR1TEST.dir\build.make:120: LABOR1TEST.exe] Error 1
mingw32-make.exe[2]: *** [CMakeFiles\Makefile2:95: CMakeFiles/LABOR1TEST.dir/all] Error 2
mingw32-make.exe[1]: *** [CMakeFiles\Makefile2:102: CMakeFiles/LABOR1TEST.dir/rule] Error 2
mingw32-make.exe: *** [Makefile:137: LABOR1TEST] Error 2

here is my code

main.cpp

#include <iostream>
#include <vector>
#include "test.hpp"

int main() {

    PersonType p;
    p.Name = "Max";
    std::vector<PersonType> TEMPVEC;
    TEMPVEC.push_back(p);
    TEMPVEC.push_back(p);
    test (TEMPVEC);
    return 0;
}

test.cpp

#include <iostream>
#include <vector>
#include <string>
#include "test.hpp"
void test(std::vector<PersonType> const &_Personen)
{
    for (auto const  &i : _Personen)
    {
    std::cout << i.Name << std::endl;
    }
}

and my test.hpp

#include <vector>
#include <string>
#ifndef LABOR1TEST_TEST_HPP
#define LABOR1TEST_TEST_HPP

using PersonType = struct {
    std::string Name;
};
void test(std::vector<PersonType> const &_Personen);
#endif //LABOR1TEST_TEST_HPP

CMakeLists.txt

cmake_minimum_required(VERSION 3.17)
project(LABOR1TEST)

set(CMAKE_C++_STANDARD 11)

add_executable(LABOR1TEST main.cpp test.cpp test.hpp)

Solution

  • My interpretation

    using PersonType = struct { ..... }; on a top-level will generate a different anonymous type in each translation unit (.cpp file, for simplicity) and then give it a name of PersonType in each translation unit. It does not matter that this statement happens inside test.hpp, all #includes are handled by preprocessor and do not matter when dealing with types.

    So, PersonType in two translation units (main.cpp and test.cpp) actually refer to different types. In effect, main.cpp expects to find test(vector<PersonType> const &) with one PersonType, and test.cpp only provides test(vector<PersonType> const&) with another PersonType, hence the linkage error.

    Is it a GCC bug?

    You can see the error better if you get rid of templates and try compiling the following two translation units together:

    using Foo = struct {};
    void test(Foo);
    int main() {
        Foo f;
        test(f);
    }
    
    using Foo = struct {};
    void test(Foo) {
    }
    

    My GCC tells me the following:

    a.cpp:2:6: error: 'void test(Foo)', declared using unnamed type, is used but never defined [-fpermissive]
        2 | void test(Foo);
          |      ^~~~
    a.cpp:1:7: note: 'using Foo = struct<unnamed>' does not refer to the unqualified type, so it is not used for linkage
        1 | using Foo = struct {};
          |       ^~~
    a.cpp:2:6: warning: 'void test(Foo)' used but never defined
        2 | void test(Foo);
          |      ^~~~
    

    However, replacing using Foo = struct {}; with typedef struct {} Foo; apparently works, as well as switching from my GCC to my Clang.

    M.M from comments suggested that typedef should work because of [basic.link]/4.3

    a named class ([class.pre]), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes ([dcl.typedef]);

    It may get interpreted by GCC too literally(?) to exclude using, although [dcl.typedef]/2 says that the name introduced by using should have the same semantics as typedef.

    Surprisingly, it was even asked on StackOverflow before!

    Regarding your problem

    He wants me to define the structure with using Persontype= struct{}; and not a normal definition of a structure.

    If the text above is true, that's quite a strange requirement. It makes it impossible to link different translation units against PersonType. I'm wondering what's the rationale behind, maybe you're not supposed to actually expose PersonType beyond a single translation unit? Consider consulting with your TA or a professor.