我在一个头文件上有一个class
,它的成员在一个pimpl类中定义。这个想法是我使用这个方法(基本上是std::aligned_storage_t
和一个指针,但在声明对象时必须指定类的大小和对齐方式(在堆栈上分配pimpl类。我想让代码交叉编译器,所以猜测不是一个选项,所以我定义了两个private
static constexpr
函数:impl_size
和impl_align
,它们在相应的源文件上定义,基本上返回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行是在报头上定义CCD_ 9的地方。
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
时,您没有定义它。
必须立即在类定义中内联提供定义。
您的代码有几个问题,我觉得很奇怪。";PImpl";习惯用法是将接口与其实现解耦,通常是为了在实现发生变化时更容易进行编译。但是,通过在接口类中定义struct impl
,并在同一类中的模板中使用它,实际上就是在强制实现与接口耦合。
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
是一个完整的类型。如果struct impl
是嵌套的cless,那么这无论如何都是必需的。
您似乎试图在这里进行某种类型擦除(或者您需要impl
的大小和对齐是否还有其他充分的理由?(,但无意中引入了对所涉及的实现和类型的大量依赖。
我建议如下:在命名空间范围内转发声明impl
类,并简单地使用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;
}
实时演示