【问题标题】:out of class definition of function template using expression-SFINAE使用表达式-SFINAE 的函数模板的类定义
【发布时间】:2020-05-17 11:08:53
【问题描述】:

我正在尝试实现一个简单的序列化程序类,它有一个序列化函数,该函数将实际序列化分派到不同的重载函数模板,在编译时使用带有 decltype 的表达式-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

问题是,这段代码无法编译,因为编译器抱怨本机类型的 Serialize 的类外定义在类内没有相应的声明:

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>())

如果我将内联定义放在类中,它编译得很好。它发生在 GCC 和 VC++ 中。

编辑

如果我在成员函数模板声明之前声明 SerializeNative 成员函数,则代码可以正常工作,似乎因为对 SerializeNative 函数的调用位于 Serialize 函数头(在 decltype 中)内部,所以它需要查看声明。

【问题讨论】:

标签: c++ templates overloading sfinae decltype


【解决方案1】:

编译器无法将定义与声明匹配的原因如下:

[basic.lookup.unqual]/p7:

X 的完整类上下文 ([class.mem]) 之外的类 X 的定义中使用的名称应以下列方式之一声明:

[class.mem]/p6:

一个类的完整类上下文是一个

  • 函数体([dcl.fct.def.general]),
  • 默认参数,
  • noexcept 说明符,或
  • 默认成员初始化器

在类的成员规范内。

即在声明点:

template <typename T>
auto Serialize(const T &value) const -> decltype(SerializeNative(value), std::declval<void>());

名称查找未找到名称SerializeNative,因为SerializeNative在使用后声明,而在定义中找到,导致不匹配。

为了在表达式 SFINAE 中使用 SerializeNative,您需要在 Serialize 的返回类型中使用它们的名称之前声明私有虚函数。

SerializeNative(value) 的错误不会立即报告,因为只要知道value 的类型,就可能在依赖于参数的查找中找到该函数。

【讨论】:

    猜你喜欢
    • 2020-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    • 2021-05-24
    • 1970-01-01
    相关资源
    最近更新 更多