4

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)
M.M
  • 138,810
  • 21
  • 208
  • 365
  • 4
    You need to link `test.cpp` too. Please show you CMakeLists.txt – Yksisarvinen Apr 06 '21 at 09:55
  • 1
    I have changed the question now. + He does not want us to include test.cpp in main – Momme Sherif Apr 06 '21 at 10:03
  • Linking and including are different things. – underscore_d Apr 06 '21 at 10:06
  • 1
    @M.M It's interesting that behavior differs between compilers. `clang++` works for me, but not `g++`. Aren't you on Mac by any chance and have `g++` aliased to `clang++`? – yeputons Apr 06 '21 at 10:15
  • 1
    @yeputons I thought `using T = .....;` was meant to behave identically to the equivalent `typedef`. Check the standard now – M.M Apr 06 '21 at 10:16
  • 1
    @M.M Wow, it's equivalent, but yields different results on my compiler. Interesting. – yeputons Apr 06 '21 at 10:17
  • 2
    @yeputons ok, it's in C++17 [basic.link]/4.3 which says that an unnamed class defined in a *typedef-declaration* behaves for linkage purpose as if the class had the typedef name. So perhaps g++ is taking this literally and only applying that paragraph to `typedef` and not `using` ? I doubt that was the intent of the language, probably that text was written before `using` was even added to the language and never been revisited – M.M Apr 06 '21 at 10:24
  • 2
    @yeputons [dcl.typedef]/2 says explicitly "Such a typedef-name has the same semantics as if it were introduced by the typedef specifier." So I would tend to call this a g++ bug, although technically the standard is defective in that it gives contradictory requirements – M.M Apr 06 '21 at 10:31
  • 3
    @MommeSherif congratulations on discovering a defect in the Standard and a possible g++ bug. Not everyone can say they have done that :) – M.M Apr 06 '21 at 10:33
  • 2
    @M.M Look what I've found: https://stackoverflow.com/questions/48613758/using-vs-typedef-is-there-a-subtle-lesser-known-difference – yeputons Apr 06 '21 at 10:38
  • 2
    @yeputons Looks like I have to withdraw my congratulation to OP. And should really close as duplicate – M.M Apr 06 '21 at 10:40
  • 1
    Wow that's an old problem! , i hope it gets fixed soon. Sad that's I was not the first xD .. Thank you a lot – Momme Sherif Apr 06 '21 at 10:43

1 Answers1

2

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.

yeputons
  • 8,478
  • 34
  • 67
  • 1
    Actually, after a bit of research, I'm not so sure in my interpretation: g++ and clang++ yield different results for me, as well as changing between `using PersonType = struct {.....}` and `typedef struct {.....} PersonType`. Needs further investigation. – yeputons Apr 06 '21 at 10:21
  • thats true i tried it with the visual studio c++ compiler in the toolchain of cmake (in clion) and it worked normal! And doesn't work in Linux either with the g++ or gcc compiler – Momme Sherif Apr 06 '21 at 10:28
  • Thanks man! you are a hero, i will show him this tomorrow and ask him again. Really thanks a lot !!!! – Momme Sherif Apr 06 '21 at 10:37