0

I'm surprised to not have found an answer to my question here already. If I overlooked it feel free to point me there.

Edit: I got a notification that this could be a possible duplicate of Why can templates only be implemented in the header file? While this contains a lot of helpful information on how classes templated classes are treated by the compiler I still don't find information on how to treat static constant members needed by the class that are actually only needed once for all possible template instantiations.

My use-case is a templated floating point number to string conversion class. One member function should be a function that creates numbers appended with si-prefixes. Therefore some lookup array with the prefix characters is needed - this array is unrelated to whichever templated floating point type is actually chosen. I did something like that:

// Float2String.h
#include <string>

template <typename FloatType>
class Float2String 
{

public:

    //...

    static std::string withSIPrefix (FloatType floatNumber, int numSignificantDigits)
    {
         // scale the number, convert it to a string and compute the idx to pick the right prefix...

         return floatNumberScaledAndConvertedToString + siPrefixes[idx];
    }

private:
    static constexpr char[11][3] siPrefixes = {"f", "p", "n", "μ", "m", "", "k", "M", "G", "T", "P"};
};

// Float2String.cpp
#include "Float2String.h"

template <typename FloatType>
constexpr char Float2String<FloatType>::siPrefixes[11][3];

When trying to actually use it e.g. to convert a double number, I get the following linker error:

Error:Undefined symbol 'Float2String<double>::siPrefixes' referenced from:
Error:  Float2String<double>::withSIPrefix(double, int) in Main.o

I'm on Mac OS with Clang and compile with C++ 14 enabled.

Questions: How do I do it right? And maybe could this be done better with a different approach?

PluginPenguin
  • 1,576
  • 11
  • 25
  • 1
    Possible duplicate of [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – mch Sep 25 '18 at 08:15
  • Why do you need to define anything in the `.cpp` file at all? https://godbolt.org/z/MLUmDm – Evg Sep 25 '18 at 08:58
  • @Evg while your code snippet compiles fine on godbolt it still throws the same linker error on my machine when trying to compile the very same code in a simple command line project containing just one source file. I am used that static members need to be defined in a separate compile unit, this is why I put them into a separate .cpp file – PluginPenguin Sep 25 '18 at 09:17
  • You could put your static member variable inside a static method that just returns it. – Mihayl Sep 25 '18 at 10:05

1 Answers1

2

Perhaps you can define your siPrefixes outside the template calss. It does not depend on your template type.

You can define it directly in your cpp file and move method withSIPrefix implementation in your cpp file also.

Gojita
  • 490
  • 4
  • 10
  • could you elaborate how you would actually implement this solution with a short code example? – PluginPenguin Sep 25 '18 at 08:44
  • Forget about the second part of my answer. Just transfer your code from the Float2Type.cc to Float2Type.h (this code: template constexpr char Float2String::siPrefixes[11][3];) If you want to keep your code in .cc file you have to instanciate explicitly your siPrefix attribute for every template you will use. This is why I will export it to the .h file, and even, if you don't care about the siPrefix to be private, take this variable outside the class – Gojita Sep 25 '18 at 09:30