0

I am working on a project with several modules and I'm getting a lot of duplicate symbols errors. I've tried to create a stripped-down version of my project to diagnose the error.

I have the code and dependency structure below. Every .hpp and .tpp file has header guards, but I'm getting duplicate symbols errors from MyClass.cpp and main.cpp.

Is my inclusion structure ok, and any ideas how I can go about fixing the error?

inclusion structure

CMakeLists.txt

cmake_minimum_required(VERSION 3.18.0)
project("duplicate symbols" LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
add_executable(main main.cpp MyClass.cpp)

Base.hpp

#pragma once

template <typename T>
struct Base { void hello_base() const; };

#include "Base.tpp"
#include "BaseSpec.hpp"

Base.tpp

#pragma once

#include <iostream>

template <typename T>
void Base<T>::hello_base() const { std::cout << "hello base" << std::endl; }

BaseSpec.hpp

#pragma once

template <>
struct Base<int> { void hello_base() const; };

#include "BaseSpec.tpp"

BaseSpec.tpp

#pragma once

#include <iostream>

void Base<int>::hello_base() const
{
    std::cout << "Hello base int" << std::endl;
}

MyClass.hpp

#pragma once

#include "Base.hpp"

struct MyClass
{
    void hello_myclass() const;
};

MyClass.cpp

#include "MyClass.hpp"

void MyClass::hello_myclass() const
{
    std::cout << "hello myclass" << std::endl;
}

main.cpp

#include "MyClass.hpp"

int main() {}

The error message is:

duplicate symbol 'Base<int>::hello_base() const' in:
    CMakeFiles/main.dir/main.cpp.o
    CMakeFiles/main.dir/myclass.cpp.o
Paradox
  • 1,935
  • 1
  • 18
  • 31
  • Inclusion order has nothing to do, whatsoever, with the root causes of duplicate symbols. You need to show a [mre]. – Sam Varshavchik May 17 '21 at 01:43
  • Ok, I'll try and provide one, it's just gonna be a lot of code. – Paradox May 17 '21 at 01:45
  • It should not be "a lot of code". Maybe 20-30 lines of code, at the most. – Sam Varshavchik May 17 '21 at 01:48
  • 1
    No it's not. A lot of code would kinda go against the *minimal*. Read the link. What MRE is really trying to do is expose you to a powerful troubleshooting technique. You make a MRE and most of the time the problem goes away. Make the MRE before you post the question and the vast majority of the time you won't need to ask the question. Know how to isolate errors before you enter the workforce and you are almost infinitely more valuable than your classmates who don't. – user4581301 May 17 '21 at 01:49
  • Ok I've added the code. I think it's got something to do with the BaseSpec specialization, because if I remove `#include BaseSpec.hpp` from Base.hpp, it seems to work. Any ideas how I can fix it? – Paradox May 17 '21 at 02:08

2 Answers2

2

Both of the .cpp files include, indirectly, BaseSpec.tpp which defines:

void Base<int>::hello_base() const

That's your violation of the One Definition Rule, that results in a duplicate symbol link failure.

Removing BaseSpec.tpp, and replacing it with a .cpp file, with the same contents, should make the linkage error go away.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Thanks! Is this mainly because it is a full specialization as opposed to a template implementation? Btw I made the following changes: 1. BaseSpec.tpp -> BaseSpec.cpp, 2. BaseSpec.cpp includes BaseSpec.hpp instead of other way around, 3. Add BaseSpec.cpp to CMakeLists add_executable. But then I get `error: explicit specialization of undeclared template struct Base`, so I had to add `#include "Base.hpp"` to the beginning of BaseSpec.hpp. It now works but there is a two-way include between Base.hpp a BaseSpec.hpp. Is this a decent solution? – Paradox May 17 '21 at 02:31
  • Yes, a full specialization is no different than a non-template function, for the purposes of the One Definition Rule. – Sam Varshavchik May 17 '21 at 11:02
1
  1. make sure you use #ifdef XXX_H_ in a header
  2. you cannot define a variable in header, only can you declare.
phoebe.mu
  • 21
  • 4
  • If the asker is using one of the major compilers (not information we're given, unfortunately) and their build system isn't overly complex (Doesn't look like it is) the `#pragma once` should be taking care of point 1. – user4581301 May 17 '21 at 02:07
  • You can define a variable in a header. You just have to do it carefully. – user4581301 May 17 '21 at 02:08
  • Thanks for the advice, but all I've got in the headers are classes. (I've updated the post.) – Paradox May 17 '21 at 02:10