【问题标题】:Define `static constexpr` function on source file在源文件上定义`static constexpr`函数
【发布时间】:2020-12-13 20:14:37
【问题描述】:

我在一个头文件上有一个class,它的成员在一个 pimpl 类中定义。这个想法是我使用this方法(基本上std::aligned_storage_t和一个指针,但是在声明对象时必须指定类的大小和对齐方式)在堆栈上分配pimpl类。我想让代码交叉编译,所以猜测不是一个选项,因此我定义了 2 个privatestatic constexpr 函数:impl_sizeimpl_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 行是在标题中定义 impl_size 的位置。

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);
}

【问题讨论】:

  • 请编辑您的帖子并创建一个minimal reproducible example。我们无法修复我们看不到的代码。
  • 我添加了一个简约的例子。如果还不够,我很乐意将其“扩展”为更完整的。
  • 显然,编译器需要在编译时知道impl_size() 的值,为此它需要在包含header.hpp 的任何位置查看其主体。
  • @TerensTare MCVE 必须始终完整。没有例外。从一开始就这样做,这样我们就不会像在每天收到的其他问题上那样来回询问,浪费时间

标签: c++ static c++17 constexpr c++20


【解决方案1】:

它是这么说的:你在声明constexpr时没有定义你的成员函数。

您必须立即提供定义,内联在类定义中。

【讨论】:

    【解决方案2】:

    您的代码有几个问题让我觉得很奇怪。 “PImpl”习惯用法的重点是将接口与其实现分离,通常是为了在实现更改时更容易编译。但是,通过在接口类中定义 struct impl 并在同一类中的模板中使用它,实际上是在强制实现与接口耦合。

    sizeofalignof 需要一个完整的类型,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_sizeimpl_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 类,并简单地使用 std::unique_ptr&lt;impl&gt; 来实现您的实现。然后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;
    }
    

    Live Demo

    【讨论】:

    • 感谢您的回答。我试图避免堆分配(这是一个“有趣”的项目),但我想我会使用智能指针。
    • 可以直接将impl对象放置在type类中的非堆分配存储块内,但您需要硬编码impl 对象的大小和对齐方式(或其上限)以使其正常工作(因为您无法获得不完整类型的大小或对齐方式)。如果您喜欢极致性能,那可能会很有趣,但出于内存安全和可维护性的原因,我不鼓励这样做。
    • 这是std::aligned_storage_t 使用的想法,但我想我最好使用其他东西而不是std::aligned_storage_t。再次感谢
    • @TerensTare 我很好奇,我想我有一个相对安全的放置新 PImpl 的工作演示:coliru.stacked-crooked.com/a/9237399182be0d1f 在编译时检查大小和对齐方式。如果检查失败,您可以通过简单地定义一个宏 GIVE_ME_THE_SIZE 来查询它们,并使用输出来更正 aligned_storage_t
    • 上限也应该起作用,例如impl_size => max_impl_sizestatic_assert(sizeof(impl) &lt;= max_impl_size);
    猜你喜欢
    • 2020-12-11
    • 1970-01-01
    • 1970-01-01
    • 2013-04-19
    • 1970-01-01
    • 1970-01-01
    • 2014-11-29
    • 2019-02-07
    相关资源
    最近更新 更多