How to specialize template member function?
Asked Answered
S

2

9

I have the following template method:

struct MyStruct
{
  // ...
  template<typename T>
  void readField(std::istream& in, T& data)
  {
      read(in, data);
      data = ntohl(data);
  }
};

template<>
void MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data)
{
    read(in, data);
}

But I get those strange linker errors:

/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/exception:62: multiple definition of `void MyStruct::readField(std::basic_istream >&, unsigned char&)' ../Lib/obj/MyStruct.o:/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/exception:62: first defined here collect2: ld returned 1 exit status make: *** [Lib] Error 1

How can I specialize this member function?

EDIT

This approach works:

struct MyStruct
{
  // ...
  template<typename T>
  void readField(std::istream& in, T& data)
  {
    read(in, data);
    data = ntohl(data);
  }

  void readField(std::istream& in, uint8_t& data)
  {
    read(in, data);
  } 
};

or with inlines or specializing it outside the class with inline

struct MyStruct
{
  // ...
  template<typename T>
  void readField(std::istream& in, T& data)
  {
      read(in, data);
      data = ntohl(data);
  }
};

template<>
inline void MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data)
{
    read(in, data);
}
Sinecure answered 26/1, 2016 at 15:21 Comment(10)
Function specialization is a regular function, not a template. As any other function, it must be declared in a header file and implemented in a source file; or else, implemented in a header file but marked inline.Mcdonnell
@IgorTandetnik This approach works. You can submit it as an answer and I can accept it. Although I can define this method in the header file without inline. Why is that so?Sinecure
I can define this method in the header file without inline Clearly you cannot, or you woudln't be here asking questions. You get a linker error when you try, don't you? Perhaps I misunderstand what you are trying to say.Mcdonnell
@IgorTandetnik Ok so what I meant was that I can define the method (without any templates/specialization) in the header file inside the class without inline. What I can do is as you mentioned specialize it with inline but only outside the class definition. Thanks a lotSinecure
An in-class definition of a member function is implicitly inline.Mcdonnell
@IgorTandetnik That I didn't know! :)Sinecure
@Igor, Patryk is saying (I think) that the specialization is not within the struct MyStruct, but still in the same header file. I just tried this an it compiled without error. I will update answer below.Claus
@Sinecure I tested in the header file, but without inline, and no errors.Claus
@nabla Make a project that consists of at least two source files that both include that header. If you only include it once, then you only have one definition, and no conflict. The problem is not "the function definition is in header file" per se - it's rather "the function is defined more than once in a program". See also en.cppreference.com/w/cpp/language/definition , and in particular "One Definition Rule" section thereof.Mcdonnell
@IgorTandetnik you're right, forgot about that!Claus
C
12

As Igor mentioned, you can implement the generic version in the header file, and then the specialization in the cpp file, for example:

// MyStruct.h

struct MyStruct {
  // ...
  template <typename T>
  void readField(std::istream& in, T& data) {
    read(in, data);
    data = ntohl(data);
  }
};

Then in the cpp file you can implement the specialization, for example:

// MyStruct.cpp

template <>
void MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data) {
  read(in, data);
}

Update: After reading the comments, the specialization can also be in the same header file as the primary template, but not within the struct, for example (I verified this by compiling and running a similar example without error):

// MyStruct.h

struct MyStruct {
  // ...
  template <typename T>
  void readField(std::istream& in, T& data) {
    read(in, data);
    data = ntohl(data);
  }
};  

template <>
inline void MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data) {
  read(in, data);
}

// End MyStruct.h
Claus answered 26/1, 2016 at 15:43 Comment(1)
When implementing the specialization in the source file, you need to declare it in the header. As in template <> void MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data); The calling code needs to know that the specialization exists, even if it doesn't need to see its actual implementation.Mcdonnell
S
4

You can inline the specialization to avoid multiple definitions.

template<>
inline void MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data)
{
    read(in, data);
}

Just to be complete, the other option you have is to create an implementation in the .cpp file, and export the implementation through the header.

//MyStruct.h

#ifdef MYSTRUCT_EXPORTS
#ifdef __GNUC__
#ifndef __linux__
#define MYSTRUCT_API __attribute__ ((dllexport))
#else
#define MYSTRUCT_API __attribute__ ((visibility ("default")))
#endif
#else
#define MYSTRUCT_API __declspec(dllexport)
#endif
#else
#ifdef __GNUC__
#ifndef __linux__
#define MYSTRUCT_API __attribute__ ((dllimport))
#else
#define MYSTRUCT_API __attribute__ ((visibility ("default")))
#endif
#else
#define MYSTRUCT_API __declspec(dllimport)
#endif
#endif

template<>
void MYSTRUCT_API MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data);


//MyStruct.cpp

template<>
void MyStruct::readField<uint8_t>(std::istream& in, uint8_t& data)
{
    read(in, data);
}
Santa answered 26/1, 2016 at 15:42 Comment(2)
This works :) but we must remember that it only works with specialization outside the class definitionSinecure
@Sinecure yea, I've added an example for how to export an implementation, which would work for specializations inside the class definition as well.Santa

© 2022 - 2024 — McMap. All rights reserved.