如何使用 C++ 中的 out 参数分配给常量变量?

How do I assign to a const variable using an out parameter in C++?

提问人:Freddy Cansick 提问时间:4/6/2022 更新时间:4/6/2022 访问量:1179

问:

在类头文件中,我声明了一个 .Texture.hstatic const int

static const int MAX_TEXTURE_SLOTS;

在 中,我将变量定义为 。Texture.cpp0

const int Texture::MAX_TEXTURE_SLOTS = 0;

现在在类的构造函数中,我尝试使用 out 参数分配给变量,但这显然不会编译为 a 而不是 .Window.cpp&Texture::MAX_TEXTURE_SLOTSconst int*int*

glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &Texture::MAX_TEXTURE_SLOTS);

我尝试使用 ,但在运行时遇到分段错误。const_cast

glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, const_cast<int*>(&Texture::MAX_TEXTURE_SLOTS));

我也尝试过直接投射到一个,但又一次,赛格故障。int *

glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, (int*)&Texture::MAX_TEXTURE_SLOTS);

非常感谢。

C++(英语:C++) 常数 常播

评论

6赞 Raildex 4/6/2022
为什么它不是 const 时,它首先是 const?
2赞 Adrian Mole 4/6/2022
为什么要声明它,就好像你打算改变它一样?const
0赞 WhozCraig 4/6/2022
“我尝试分配给变量”——你不能这样做来控制变量;您只能初始化它们(实际上,它们必须以一种或另一种形式初始化)。我认为你的橡皮鸭应该在这里解释一下。
0赞 KoVadim 4/6/2022
不能更改常量。只需删除const
0赞 Raildex 4/6/2022
可以这样想:例如,编译器看到“嘿,它是常量,它是 0。这个 for 循环会检查它。由于它是 const 和 0,我可以省略循环 :)”这显然是简化的,但这是编译器所期望的。它是 const 和 0。在运行时通过晦涩难懂的强制转换来更改它会破坏您的程序。

答:

1赞 paolo 4/6/2022 #1

前提是不依赖于之前调用的其他函数,则可以使用立即调用的 lambda:glGetIntegervgl*

// Texture.cpp

const int Texture::MAX_TEXTURE_SLOTS = []
{
    int maxTextureSlots{0}:
    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureSlots);
    return maxTextureSlots;
}();

评论

0赞 datenwolf 4/6/2022
不幸的是,它确实依赖于之前调用的函数,即 OpenGL 上下文的创建和绑定。此外,检索到的值可能会根据绑定的 OpenGL 上下文而更改。glGetInteger
0赞 Wyck 4/6/2022
@datenwolf 有效的观点,但是,从语言的角度来看,这与 .对于您选择的任何作用域,将放置函数结果的变量视为常量是可以的,无论该函数是否会返回不同的值。const int x = rand();
0赞 datenwolf 4/6/2022
@Wyck:问题是,该值实际上可能会发生变化。 → a 和 b 可能具有不同的值。GLint a, b; …glMakeCurrent(glrc_a, …); glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &a); …glMakeCurrent(glrc_b, …); glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &b);
0赞 Wyck 4/6/2022
@datenwolf 我完全同意你的看法。愚蠢的可能是它看起来是类的静态成员,因此将作为静态初始化的一部分接收其值,并且如果实现是单独调用,则 gl 状态在静态初始化时将无效。尽管如此,lambda 的实现可以扩展以确保状态正确,因此如何使用函数结果初始化 const 的一般方法是正确的 - 由于缺少 OpenGL init,所示的实现不足以进行静态初始化。glGetIntegerv
1赞 datenwolf 4/6/2022 #2

编辑 2:因此,既然您正在尝试抽象 OpenGL 上下文,那么您必须放弃“传统”构造函数/析构函数的习惯用语。仅供您参考(与此问题无关):OpenGL 上下文与 Windows 无关!只要一组窗口和 OpenGL 上下文相互兼容,您就可以以任何您喜欢的方式混合和匹配。但我跑题了。

处理类似情况的标准习语是使用预初始化的工厂函数。喜欢这个:

class MyOpenGLContextWrapper {
public:
    // Yes, shared_ptr; using a unique_ptr here for objects that are kind
    // of a nexus for other things -- like an OpenGL context -- just creates
    // a lot of pain and misery. Trust me, I know what I'm talkink about.
    typedef std::shared_ptr<MyOpenGLContextWrapper> ptr;

    struct constdata {
        NativeGLContextType context;
        // ...
        GLint max_texture_image_units;
        // ...
    };

    static ptr create();

protected:
    MyOpenGLContextWrapper(constdata const &cdata) : c(cdata) {};
    virtual ~MyOpenGLContextWrapper();
    constdata const c;
}

MyOpenGLContextWrapper::ptr MyOpenGLContextWrapper::create()
{
    struct object : public MyOpenGLContextWrapper {
        object(MyOpenGLContextWrapper::constdata const &cdata) : MyOpenGLContextWrapper(cdata) {}
        ~object(){}
    };

    MyOpenGLContextWrapper::constdata cdata = {};

    // of course this should all also do error checking and failure rollbacks
    cdata.context = create_opengl_context();
    bind_opengl_context(cdata.context);
    // ...
    glGetInteger(GL_MAX_TEXTURE_IMAGE_UNITS, &cdata.max_texture_image_units);

    return std::make_shared<object>(cdata);
}

EDIT: I just saw that you intend to use this to hold on to a OpenGL limit. In that case you can't do this on a global scope anyway, since those values depend on the OpenGL context in use. A process may have several OpenGL contexts, each with different limits.


On most computer systems you'll encounter these days, variables declared const in global scope will be placed in memory that has been marked as read only. You literally can't assign to such a variable.

The usual approach to implement global scope runtime constants is by means of query functions that will return from an internal or otherwise concealed or protected value. Like

// header.h
int runtime_constant();
#define RUNTIME_CONSTANT runtime_constant()

// implementation.c / .cpp
int runtime_constant_query(){
    static int x = 0;
    // NOTE: This is not thread safe!
    if( !x ){ x = determine_value(); }
    return x;
}

You can then fetch the value by calling that function.

评论

1赞 NathanOliver 4/6/2022
The if statement isn't needed here, and if returns you'll always be calling that. Instead, you can just use determine_value0static int x = determine_value(); return x;
0赞 Jeffrey 4/6/2022
Using static initialization leads to incredibly hard to debug issues on large scale project. There's no facility to specify the initialization order. The local static version, at least, fixes the initialization order
0赞 Ted Lyngmo 4/6/2022
@Jeffrey Indeed. The only question I have is why is not initialized with directly.xdetermine_value()
1赞 Freddy Cansick 4/6/2022
Thanks for the explanation on the memory side, really helpful. Unfortunately I can't upvote you cause my rep is too low but thanks anyway. I'll probably just get rid of then.const
0赞 datenwolf 4/6/2022
@TedLyngmo: It's initialized upon runtime using an conditional, because the value might actually not be available until late in process initialization; it might become available only right before is entered, or even later. Heck, it might even go as far as doing a full blown library initialization in the case x hasn't been initialized yet.main
0赞 Julien BERNARD 4/6/2022 #3

You don't.

You can't assign to a const outside of its definition. Also, using a variable where the has been ed away is UB. This also means you can't directly initialize a const variable with an output parameter. For trivial types, just output to another variable and make a const copy if you so wish.constconstconst_cast

If you were the author of the function you're calling, you would do well not to use out parameters, and then you could assign to variables directly, perhaps using structured bindings if you want to name multiple of the outputs at a time. But here, you're not.const