c++ 声明不变的、静态的“默认”成员变量/函数的“正确”方法是什么?

yhuiod9q  于 2022-12-15  发布在  其他
关注(0)|答案(1)|浏览(158)

给出一个简单的、最基本的Vector3D示例,如何定义静态默认“值”,例如vec3<T>::ones(或vec3<T>::ones())提供vec3<T>{T{1}, T{1}, T{1}}

template <class T>
struct vec3 {
    using value_type = T;
    value_type x{}, y{}, z{};
    
    constexpr vec3(const value_type e0, const value_type e1, const value_type e2) noexcept 
      : x(e0)
      , y(e1)
      , z(e2) 
    { }

    // Option 1
    [[nodiscard]] constexpr static inline vec3 zeros() noexcept { return {0, 0, 0}; }
    
    // Option 2
    [[nodiscard]] constexpr static inline vec3 x_axis() noexcept { 
        constexpr static vec3 _x_axis{1, 0, 0};
        return _x_axis;
    }
    
    // Option 3
    const static inline vec3 ones = [](){ return vec3{1, 1, 1}; }();

};

我经常看到这三种选择的变体,我有几个问题,我对这个问题的理解是否正确:

  • 选项1,据我所知,作为一个“工厂函数”为每次调用创建新的vec3示例,这是否等同于任何调用者直接使用vec3{e0, e1, e2}
  • 选项2只创建一个vec3示例一次,也就是函数第一次执行的时候。因此,编译器需要使用同步原语来确保静态初始化只发生一次。是否所有未来的调用都只返回“本地缓存”的值,而没有任何同步原语?
  • 选项3在编译时创建一个静态内联成员变量,我认为GCC允许静态内联变量声明为constexpr(这就是为什么我相信一切都发生在编译时),而clang只使用const编译(这不保证静态成员变量的编译时示例化?)。

这些解决方案之间是否还有我所遗漏的其他差异,我是否应该更喜欢这些差异?是否有其他方法可以在编译时声明静态成员?

72qzrwbm

72qzrwbm1#

你可以看看std::strong_ordering标准是如何实现的:

class strong_ordering {
    // ...
public:
    // valid values
    static const strong_ordering less;
    static const strong_ordering equal;
    static const strong_ordering equivalent;
    static const strong_ordering greater;
    // ...
};
// valid values' definitions
inline constexpr strong_ordering strong_ordering::less(ord::less);
inline constexpr strong_ordering strong_ordering::equal(ord::equal);
inline constexpr strong_ordering strong_ordering::equivalent(ord::equivalent);
inline constexpr strong_ordering strong_ordering::greater(ord::greater);

因此策略是在类中声明static const(不带初始化器),然后在类外定义inline constexpr(带初始化器),以保证编译时初始化,并允许在常量表达式中使用变量。
声明和定义需要分开的原因是,在类定义内部,类是不完整的,你不能有一个类型不完整的静态成员 definition,让一个static constexpr数据成员 in 类就是定义它。(inline隐含在这样的声明中。)因此,必须在类中有一个非定义声明,然后在类完成后定义声明,在定义声明(定义)中,可以使用constexpr
在你的例子中,它看起来像这样:

template <class T>
struct vec3 {
    using value_type = T;
    value_type x{}, y{}, z{};
    
    constexpr vec3(const value_type e0, const value_type e1, const value_type e2) noexcept 
      : x(e0)
      , y(e1)
      , z(e2) 
    { }

    static const vec3 ones;
};

template <class T>
inline constexpr vec3<T> vec3<T>::ones = {1, 1, 1};

在本例中,因为vec3是一个模板,所以我认为您甚至不需要inline

相关问题