class 使用 expression-SFINAE 定义函数模板
out of class definition of function template using expression-SFINAE
我正在尝试实现一个简单的序列化器 class,它有一个序列化函数,可以将实际序列化分派到不同的重载函数模板,在 compile-time 使用 expression-SFINAE 选择声明类型:
#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
问题是,此代码无法编译,因为编译器抱怨说 class 的原生类型序列化定义在 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>())
如果我将内联定义放在 class 中,它可以正常编译。它发生在 GCC 和 VC++ 中。
编辑
如果我在成员函数模板声明之前声明 SerializeNative 成员函数,代码工作正常,似乎因为对 SerializeNative 函数的调用是在 Serialize 函数内部 header(在 decltype 中)它需要查看声明。
编译器无法将定义与声明匹配的原因如下:
A name used in the definition of a class X
outside of a complete-class context ([class.mem]) of X
shall be declared in one of the following ways:
- before its use in class X or be a member of a base class of X ([class.member.lookup]), or
- [...]
其中 [class.mem]/p6:
A complete-class context of a class is a
- function body ([dcl.fct.def.general]),
- default argument,
- noexcept-specifier, or
- default member initializer
within the member-specification of the class.
即声明点:
template <typename T>
auto Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>());
名称查找未找到名称SerializeNative
,因为SerializeNative
是在使用后声明的,而在定义中找到,导致不匹配。
为了在表达式 SFINAE 中使用 SerializeNative
,您需要在 Serialize
的 return 类型中使用它们的名称之前声明私有虚函数。
不会立即报告 SerializeNative(value)
的错误,因为一旦知道 value
的类型,就可能会在依赖于参数的查找中找到该函数。
我正在尝试实现一个简单的序列化器 class,它有一个序列化函数,可以将实际序列化分派到不同的重载函数模板,在 compile-time 使用 expression-SFINAE 选择声明类型:
#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
问题是,此代码无法编译,因为编译器抱怨说 class 的原生类型序列化定义在 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>())
如果我将内联定义放在 class 中,它可以正常编译。它发生在 GCC 和 VC++ 中。
编辑
如果我在成员函数模板声明之前声明 SerializeNative 成员函数,代码工作正常,似乎因为对 SerializeNative 函数的调用是在 Serialize 函数内部 header(在 decltype 中)它需要查看声明。
编译器无法将定义与声明匹配的原因如下:
A name used in the definition of a class
X
outside of a complete-class context ([class.mem]) ofX
shall be declared in one of the following ways:
- before its use in class X or be a member of a base class of X ([class.member.lookup]), or
- [...]
其中 [class.mem]/p6:
A complete-class context of a class is a
- function body ([dcl.fct.def.general]),
- default argument,
- noexcept-specifier, or
- default member initializer
within the member-specification of the class.
即声明点:
template <typename T>
auto Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>());
名称查找未找到名称SerializeNative
,因为SerializeNative
是在使用后声明的,而在定义中找到,导致不匹配。
为了在表达式 SFINAE 中使用 SerializeNative
,您需要在 Serialize
的 return 类型中使用它们的名称之前声明私有虚函数。
不会立即报告 SerializeNative(value)
的错误,因为一旦知道 value
的类型,就可能会在依赖于参数的查找中找到该函数。