1

Is it possible to declare and define a const static instance of a class in the class header file.

I want to do something like this (from this similar question):

class PlaceID {

public:

    inline PlaceID(const std::string placeName):mPlaceName(placeName) {}

    const static PlaceID OUTSIDE;

private:
    std::string mPlaceName;
};

const PlaceID PlaceID::OUTSIDE = PlaceID("");

This would work if the definition of PlaceID::OUTSIDE was in a source file, but if it's in a header file that is include in multiple location it causes an link error because PlaceID::OUTSIDE is then defined multiple times.

I'd like to define it in the header file for two reasons. First, this will be part of a library and I'd like the library to be header file only.

Second and this is the most important one I want the compiler to be allowed to "inline" the uses of this instance. The class in question (not the one used as an example here) is a wrapper around a primitive type with all methods inlined in order to offer the same performance as the primitive type would. If I place the definition of this instance in a source file, the compiler will not know it's value at compilation time and won't be able to apply some optimisations.

Thanks.

Mathieu Pagé
  • 10,764
  • 13
  • 48
  • 71

2 Answers2

6

In C++17, variables can be marked as inline:

class PlaceID 
{
    inline const static PlaceID OUTSIDE{""};
    // ...
};

Before C++14, you can use a function instead:

class PlaceID 
{
    static PlaceID OUTSIDE() { return PlaceID{""}; }
    // ...
};

...or...

class PlaceID 
{
    static PlaceID OUTSIDE() 
    { 
        static PlaceID result{""};
        return result;
    }

    // ...
};

...depending on whether or not you want a single PlaceID instance.


Alternatively, you can templatize PlaceID over a dummy parameter in order to inline the definition of OUTSIDE in an header:

template <typename>
struct PlaceID_
{
    inline PlaceID_(const char*) { }
    const static PlaceID_ OUTSIDE;
};

template <typename T>
const PlaceID_<T> PlaceID_<T>::OUTSIDE{""};

using PlaceID = PlaceID_<void>;

This works because templates are implicitly inline. See How do inline variables work? for more details.

live example on wandbox.org


Also consider marking PlaceID::PlaceID(const char*) as constexpr if possible so that OUTSIDE can be marked constexpr as well. Obviously this will not work if you decide to use std::string.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
0

that is include in multiple location

You can stop your header from being included multiple times(and causing your problem(DISCLAIMER: I have not tested it.)) by this(which is commonly used by a lot of libraries)

a.hpp

#ifndef LIBRARY_A
#define LIBRARY_A
//define other things
#endif

replace LIBRARY_A for an ID of your choice(note it can't conflict with other libraries so avoid names like SOUND_SYSTEM, but rather SOUND_LIBRARY_FOO with FOO replaced by your project name/your name)