C++ 获取类成员的默认值而不创建新对象

问题描述 投票:0回答:3

让我们考虑一下定义:

struct ClassWithMember
{
    int myIntMember = 10;
}

我想获取

myIntMember
的默认值,但不要创建该类的另一个实例

// IMPOSSIBLE int myInt = ClassWithMember::myIntMember;  
// MUST AVOID int myInt = ClassWithMember().myIntMember;

我知道解决方法,但不喜欢它:

struct ClassWithMember
{
    static const int myIntMember_DEFAULT = 10;
    int myIntMember = myIntMember_DEFAULT;
}
int myInt = ClassWithMember::myIntMember_DEFAULT;

因为需要额外的线路。而且我无法定义像

static const int *const INTEGER11_DEFAULT = 0x100088855;
这样的内联静态指针,此类指针必须在
.cpp
文件中定义,
.hpp
中只是声明。我的许多类都是仅标头的,因此为此值创建多余的
.cpp
并不是一个好主意。

这里是C#的类似问题

c++ c++11 default-value static-members
3个回答
2
投票

我想,对你来说,这只是另一种解决方法,但我认为有点(只是一点点)更实用。

如果将默认值保存为静态方法返回的静态常量,则可以避免在 cpp 文件中添加额外的行。

下面的示例在模板包装器中实现了这一点(使用默认值作为带有默认值的模板参数;只是为了好玩),但模板部分只是为了避免示例中代码的重复

#include <iostream>

template <typename T, T defTpl = T{}>
struct wrapperWithDef
 {
   static T getDefVal ()
    { static T const def { defTpl }; return def; }

   T myTMember { getDefVal() };
 };

int main()
 {
   wrapperWithDef<int>          wi;
   wrapperWithDef<long, 3L>     wl;
   wrapperWithDef<int *>        wp;

   // print "0, 3, (nil)" (clang++) or "0, 3, 0" (g++)
   std::cout << wi.myTMember << ", " << wl.myTMember << ", "
      << wp.myTMember << std::endl;

   // print "5, (nil), 1" (clang++) or "5, 0, 1" (g++)
   std::cout << wrapperWithDef<unsigned, 5U>::getDefVal() << ", "
      << wrapperWithDef<long *>::getDefVal() << ", "
      << wrapperWithDef<bool, true>::getDefVal() << std::endl;

   return 0;
 }

1
投票

我称此解决方案为解决方法

struct ClassWithMember
{
    static const int myIntMember_DEFAULT = 10;
    int myIntMember = myIntMember_DEFAULT;
}
int myInt = ClassWithMember::myIntMember_DEFAULT;

对于指针来说会看起来更复杂

//.hpp
struct ClassWithMember
{
    static AnotherClass* const myMember_DEFAULT;  //=X; Assignment not allowed
    AnotherClass* myMember = myMember_DEFAULT;
}
//.cpp
AnotherClass* const MyNamespace::ClassWithMember::myMember_DEFAULT = pAnotherInstance;
//usage
auto *my = ClassWithMember::myMember_DEFAULT;

0
投票

如果您有能力拥有

constexpr
友好的结构,请考虑以下想法。
consteval
可用于在编译时强制对象初始化(和销毁)。
此示例展示了一种通过
std::string
std::string_view
设置默认值的方法 (C++20 起):https://godbolt.org/z/jxh1s8qev

#include <type_traits>

#include <array>
#include <string>

template <typename T, typename V>
consteval V _mem_type(V T::*) noexcept;

template <auto M, typename = decltype(_mem_type(M))>
struct default_value;

template <typename T, typename V, V T::*M>
struct default_value<M, std::enable_if_t<std::is_fundamental_v<V>, V>>
{
    consteval V operator()() const {
        return T{}.*M;
    }
};

// This is only possible starting from C++20
template <typename T, std::string T::*M>
struct default_value<M, std::string>
{
private:
    constexpr static auto _v = 
        []
        {
            std::array<char, (T{}.*M).size()> v{};
            auto i = v.begin();
            for (char c: T{}.*M)
                *i++ = c;
            
            return v;
        }();

public:
    consteval std::string_view operator()() const {
        return {&_v[0], _v.size()};
    }
};

template <auto M>
constexpr inline default_value<M> default_value_v{};

// Example
//////////

#include <iostream>

struct foo
{
    int i = 815;
    std::string s = "oceanic";
};

int main() {
    constexpr int iDefault = default_value_v<&foo::i>();
    std::cout << "default foo::i = " << iDefault << '\n';

    constexpr auto sDefault = default_value_v<&foo::s>();
    std::cout << "default foo::s = " << sDefault << '\n';
}

输出:

default foo::i = 815
default foo::s = oceanic

基本上,必须解决使用动态分配的类型的内容返回问题。

显然,它不适用于非

constexpr
友好的类型,因为不允许从 constexpr 上下文返回动态分配的资源。

© www.soinside.com 2019 - 2024. All rights reserved.