在源文件上定义 `static constexpr` 函数
Define `static constexpr` function on source file
我在 header 文件上有一个 class
,它的成员在 pimpl class 中定义。这个想法是我使用 this 方法(基本上是 std::aligned_storage_t
和一个指针,但是在声明 object 时必须指定 class 的大小和对齐方式)来分配堆栈上的 pimpl class。我想使代码 cross-compiler 所以猜测不是一个选项,因此我定义了 2 private
static constexpr
函数:impl_size
和 impl_align
定义在相应的源文件,基本上 return sizeof(pimpl)
和 alignof(pimpl)
。问题是我从 MSVC 收到以下错误(未在其他编译器上测试):
expression must have a constant value -- constexpr function function "Type::impl_size" (declared at line 70) is not defined
第 70 行是 impl_size
在 header 上定义的地方。
MCVE:
#include <cstddef>
template <typename T1, std::size_t N1, std::size_t N2>
struct Pimpl;
class Type
{
private:
static constexpr std::size_t impl_size() noexcept; // line 70
static constexpr std::size_t impl_align() noexcept;
struct impl {};
Pimpl<impl, impl_size(), impl_align()> data; // error happens here
};
constexpr std::size_t Type::impl_size() noexcept
{
return sizeof(Type::impl);
}
constexpr std::size_t Type::impl_align() noexcept
{
return alignof(Type::impl);
}
这就是它所说的:你在声明它时没有定义你的成员函数constexpr
。
您必须立即提供定义,内嵌在 class 定义中。
您的代码有几个问题让我觉得很奇怪。 “PImpl”习语的要点是将接口与其实现分离,通常是为了在实现更改时允许更容易的编译。但是,通过在接口 class 中定义 struct impl
并在同一 class 的模板中使用它,您实际上是在强制实现与接口相结合。
sizeof
和 alignof
需要一个完整的类型,其中 impl
似乎不是 (EDIT 在撰写本文时,impl
只是前向声明),因此即使在解决了 Type::impl_size()
和 Type::impl_align()
的问题后,您也会遇到这个问题。
对您的问题的一个直接但有点肤浅的解决方法是使 impl
成为一个完整的类型(并在声明时定义它),并使 impl_size()
和 impl_align()
到 inline static constexpr
函数中,这些函数也是当场定义的:
class type
{
// ...
private:
struct impl {
// struct definition, so that impl is a complete type
};
inline static constexpr impl_size() noexcept {
return sizeof(impl);
}
inline static constexpr impl_align() noexcept {
return alignof(impl);
}
Pimpl<impl, impl_size(), impl_align()> data;
};
这仍然有点味道,因为 impl_size
和 impl_align
是毫无意义的样板,而您的 PImpl
模板可以直接从其第一个参数获得所有相同的信息:
template<typename T>
class Pimpl {
static constexpr std::size_t Size = sizeof(T);
static constexpr std::size_t Alignment = alignof(T);
};
class type {
private:
struct impl {};
Pimpl<impl> data;
};
这当然也要求impl
是一个完整的类型。还有 this will be required anyway if struct impl
is a nested cless.
看来您正在尝试在此处进行某种类型的擦除(或者是否有其他充分的理由需要 impl
的大小和对齐方式?),但无意中引入了很多依赖项关于实现和涉及的类型。
我建议如下:在命名空间范围内转发声明您的 impl
class,并简单地使用 std::unique_ptr<impl>
来实现。然后 impl
可以在你的实现文件中定义。请注意 std::unique_ptr
不需要完整类型。
#include <iostream>
#include <memory>
// header.hpp
struct impl;
class type {
public:
type();
void doThing();
private:
std::unique_ptr<impl> m_pImpl;
};
// source.cpp
struct impl {
void doThingImpl() {
std::cout << "Did a thing";
}
};
type::type()
: m_pImpl(std::make_unique<impl>()) {
}
void type::doThing() {
m_pImpl->doThingImpl();
}
int main(){
auto t = type{};
t.doThing();
return 0;
}
我在 header 文件上有一个 class
,它的成员在 pimpl class 中定义。这个想法是我使用 this 方法(基本上是 std::aligned_storage_t
和一个指针,但是在声明 object 时必须指定 class 的大小和对齐方式)来分配堆栈上的 pimpl class。我想使代码 cross-compiler 所以猜测不是一个选项,因此我定义了 2 private
static constexpr
函数:impl_size
和 impl_align
定义在相应的源文件,基本上 return sizeof(pimpl)
和 alignof(pimpl)
。问题是我从 MSVC 收到以下错误(未在其他编译器上测试):
expression must have a constant value -- constexpr function function "Type::impl_size" (declared at line 70) is not defined
第 70 行是 impl_size
在 header 上定义的地方。
MCVE:
#include <cstddef>
template <typename T1, std::size_t N1, std::size_t N2>
struct Pimpl;
class Type
{
private:
static constexpr std::size_t impl_size() noexcept; // line 70
static constexpr std::size_t impl_align() noexcept;
struct impl {};
Pimpl<impl, impl_size(), impl_align()> data; // error happens here
};
constexpr std::size_t Type::impl_size() noexcept
{
return sizeof(Type::impl);
}
constexpr std::size_t Type::impl_align() noexcept
{
return alignof(Type::impl);
}
这就是它所说的:你在声明它时没有定义你的成员函数constexpr
。
您必须立即提供定义,内嵌在 class 定义中。
您的代码有几个问题让我觉得很奇怪。 “PImpl”习语的要点是将接口与其实现分离,通常是为了在实现更改时允许更容易的编译。但是,通过在接口 class 中定义 struct impl
并在同一 class 的模板中使用它,您实际上是在强制实现与接口相结合。
sizeof
和 alignof
需要一个完整的类型,其中 (EDIT 在撰写本文时,impl
似乎不是 impl
只是前向声明),因此即使在解决了 Type::impl_size()
和 Type::impl_align()
的问题后,您也会遇到这个问题。
对您的问题的一个直接但有点肤浅的解决方法是使 impl
成为一个完整的类型(并在声明时定义它),并使 impl_size()
和 impl_align()
到 inline static constexpr
函数中,这些函数也是当场定义的:
class type
{
// ...
private:
struct impl {
// struct definition, so that impl is a complete type
};
inline static constexpr impl_size() noexcept {
return sizeof(impl);
}
inline static constexpr impl_align() noexcept {
return alignof(impl);
}
Pimpl<impl, impl_size(), impl_align()> data;
};
这仍然有点味道,因为 impl_size
和 impl_align
是毫无意义的样板,而您的 PImpl
模板可以直接从其第一个参数获得所有相同的信息:
template<typename T>
class Pimpl {
static constexpr std::size_t Size = sizeof(T);
static constexpr std::size_t Alignment = alignof(T);
};
class type {
private:
struct impl {};
Pimpl<impl> data;
};
这当然也要求impl
是一个完整的类型。还有 this will be required anyway if struct impl
is a nested cless.
看来您正在尝试在此处进行某种类型的擦除(或者是否有其他充分的理由需要 impl
的大小和对齐方式?),但无意中引入了很多依赖项关于实现和涉及的类型。
我建议如下:在命名空间范围内转发声明您的 impl
class,并简单地使用 std::unique_ptr<impl>
来实现。然后 impl
可以在你的实现文件中定义。请注意 std::unique_ptr
不需要完整类型。
#include <iostream>
#include <memory>
// header.hpp
struct impl;
class type {
public:
type();
void doThing();
private:
std::unique_ptr<impl> m_pImpl;
};
// source.cpp
struct impl {
void doThingImpl() {
std::cout << "Did a thing";
}
};
type::type()
: m_pImpl(std::make_unique<impl>()) {
}
void type::doThing() {
m_pImpl->doThingImpl();
}
int main(){
auto t = type{};
t.doThing();
return 0;
}