I'm trying to implement a simple serializer class, which has a Serialize function that dispatches the actual serialization to different overloaded function templates, selected at compile-time using expression-SFINAE with decltype:
#ifndef SERIALIZER_H
#define SERIALIZER_H
#include <string>
#include <utility>
/**** base/primary template for partial/explicit specialization on serializable classes ****/
template <typename T>
struct SerializeHelper;
/**** base abstract serialize class ****/
class Serializer
{
public:
// main serializer member function template
template <typename T>
void Serialize(const std::string &name, const T &value) const;
private:
virtual void Prepare(const std::string &name) const = 0;
virtual void Finalize(const std::string &name) const = 0;
// natively supported types
template <typename T>
auto Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>());
// use static function in class template specialized for type (partial or explicit specialization)
template <typename T>
auto Serialize(const T &value) const -> decltype(SerializeHelper<T>::Serialize(*this, value), std::declval<void>());
// use serializable type interface
template <typename T>
auto Serialize(const T &value) const -> decltype(value.Serialize(*this), std::declval<void>());
private:
// virtual functions for natively supported types
virtual void SerializeNative(int value) const = 0;
virtual void SerializeNative(float value) const = 0;
virtual void SerializeNative(double value) const = 0;
virtual void SerializeNative(const std::string &value) const = 0;
protected:
Serializer() = default;
};
template <typename T>
void Serializer::Serialize(const std::string &name, const T &value) const
{
Prepare(name);
Serialize(value);
Finalize(name);
}
// natively supported types
template <typename T>
auto Serializer::Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>()) // COMPILER ERROR
{
SerializeNative(value);
}
// use serialize function specialized for type
template <typename T>
auto Serializer::Serialize(const T &value) const -> decltype(SerializeHelper<T>::Serialize(*this, value), std::declval<void>())
{
SerializeHelper<T>::Serialize(*this, value);
}
// use serializable type interface
template <typename T>
auto Serializer::Serialize(const T &value) const -> decltype(value.Serialize(*this), std::declval<void>())
{
value.Serialize(*this);
}
#endif // SERIALIZER_H
Problem is, this code doesn't compile, since the compiler complains that the out of class definition of Serialize for native types doesn't have a corresponding declaration inside the class:
In file included from main.cpp:1:
serializer.hpp:53:6: error: no declaration matches 'decltype ((((const Serializer*)this)->Serializer::SerializeNative(value), declval<void>())) Serializer::Serialize(const T&) const'
auto Serializer::Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>())
If I put the inline definition inside the class it compiles fine. It happens with GCC and VC++.
EDIT
the code works fine if I declare the SerializeNative member functions before the member function template declaration, seems like since the call to the SerializeNative function is inside the Serialize function header (in decltype) it needs to see the declaration.