-2

Suppose I have a header only library. I have simplified it to something like this.

Header only library Foo.hpp

#ifndef FOO_HPP
#define FOO_HPP

struct Foo{
    static const int A;
};

const int Foo::A = 100;

void SomeMethod(){
   // do some stuff
}
#endif

Then, I have parent class (Parent.hpp and Parent.cpp):

#ifndef PARENT_HPP
#define PARENT_HPP

#include "Foo.hpp"

struct Parent {
        virtual void my_method();
};
#endif

Child class (Child.hpp and Child.cpp)

#ifndef CHILD_HPP
#define CHILD_HPP

#include "Parent.hpp"
#include "Foo.hpp"

struct Child : Parent{
        void my_method();
};
#endif  

In my_method(), I just print the Foo::A variable.

In the real code base, I have template in the header only library.

When I compile this, I got a "multiple definition" error. How to fix this?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Bharata
  • 685
  • 3
  • 11
  • 23
  • You should provide a proper working example, and the exact error message(s) you're getting. Also, say which compiler you're using and which version of the language standard. – einpoklum Jan 23 '21 at 11:37
  • I tried to provide a complete (?) set of alternatives for you to consider. – einpoklum Jan 23 '21 at 17:12

2 Answers2

0

Replace

class Foo {
    
    public:
        static const int A;
    
};

const int Foo::A = 100;

with

class Foo {
    
    public:
        static const int A = 100;
    
};
einpoklum
  • 118,144
  • 57
  • 340
  • 684
idmean
  • 14,540
  • 9
  • 54
  • 83
  • This is just a simplified case, but some other method is also defined which is not in the class. Those methods are template method. – Bharata Jan 23 '21 at 11:06
  • @Bharata Well, you must inline everything you define in a header. Otherwise you'll get linker errors. – idmean Jan 23 '21 at 11:07
  • I have added the example of SomeMethod in the header hpp file. Is there any way to solve this? – Bharata Jan 23 '21 at 11:10
  • @Bharata change `void SomeMethod(){` to `inline void SomeMethod(){` – Richard Critten Jan 23 '21 at 11:10
  • Ok, I will try it. – Bharata Jan 23 '21 at 11:11
  • When I change into this : static const int A = 100; I got undefined reference. – Bharata Jan 23 '21 at 11:19
  • @Bharata Please post a [mcve] that is complete and asks a single question. This site is not a discussion forum and questions should not be extended (to different topics) except in rare circumstances. – Richard Critten Jan 23 '21 at 11:26
  • Well, I have tried the answer to idmean and it doesn't work. But I have fixed it with adding const int Const::A. As for your suggestion for inline method, I haven't tried it. – Bharata Jan 23 '21 at 11:28
0

You have chosen the "non-inline" mode of initializing your static member. That is perfectly legitimate, but - in this case, you need to have exactly one translation unit (e.g. compiled .cpp file) defining your member. Otherwise, the C++ linker sees one definition of Foo::A in parent.o and a second definition in child.o, and those conflict.

Thus,

Solution 1: Move the definition to another file

  1. Create a Foo.cpp, which includes Foo.hpp and defines Foo::A.
  2. Delete the definition from the header, so it isn't repeated in multiple locations

Solution 2: Make the definition conditional

This is not really recommended, but it does work.

  1. Surround the definition like so:

    #ifdef FOO_A_DEFINITION
    const int Foo::A = 100;
    #endif
    
  2. Create a Foo.cpp, which defines #FOO_A_DEFINITION and then includes Foo.hpp

This has the detriment of using macros, but the benefit of human users seeing the definition from the header.

Solution 3: Initialize in class definition

So, you are asking yourself "Why should they conflict? I want the compiler to know that they're the same variable!"

One way to do this to initialize the static member within the class body. Since this is a simple type, that's possible even with older versions of the language standard. This is also @idmean's suggested solution:

class Foo {
    public:
        static const int A = 100;
};

There's a caveat here, which is that you're "not really" defining the static member this way. You can use it, but not in every way you would use a variable defined with solutions (1.) or (2.). See discussion here:

See a discussion of this point here:

Defining static const integer members in class definition

Solution 4: Inline your static member

This solution is another way to tell the compiler "it's just one definition for everybody", which only works in C++17 or later:

#include <iostream>
class Foo {
    public:
        inline static const int A = 100;
};

This looks very similar to Solution 3, but actually does something very different!

With this solution, you're actually defining the static member multiple times - in each translation unit - but are telling the compiler to only keep a single definition when linking and encountering many of them.

Solutions (1.) and (2.) behave differently from this solution (4.) when you use dynamically-linked libraries. See:

Where to initialize static const member in c++17 or newer?

for an explanation.

einpoklum
  • 118,144
  • 57
  • 340
  • 684