7

我正在尝试在我的项目中实现一个 C++ 库的使用,该库没有在其访问函数上使用 const 修饰符。到目前为止,我一直在我的所有代码中使用 const,但是这个新库导致了两个主要问题:

  1. 如果这些参数是库定义的类型,则参数作为 const 引用传递的函数不能使用参数的访问函数。

  2. 具有由库定义的类型的成员对象的类不能在 const 函数中使用这些对象的访问函数。

克服这个问题的最佳方法是什么?最简单的解决方案是简单地从我的代码中删除所有对 const 的使用,但这会非常令人沮丧。

附加信息:在这种情况下,我确实可以访问源代码,并且可以看到访问函数不会修改任何内容。我省略了这些信息,因为我也对更一般的情况感兴趣。对于我的情况,const_cast似乎是要走的路

PS图书馆作家不是邪恶的!他友好地开源了更多的粗略和现成的代码。正如其他人所指出的那样,我可以放弃图书馆并使用更专业的东西。但是,对于这个时间有限的小型项目,该库的接口简单性使其成为最佳选择。

4

5 回答 5

5

判断库中的函数是否实际修改任何内容有多容易?

如果很容易分辨,而他们没有,那么您可以const_cast将 const 指针/引用指向非 const 并调用库函数。您可能希望在库类周围放置一个包装器来为您执行此操作,这既乏味又冗长,但会从您的类中获取该代码。这个包装器可能是一个添加一些 const 访问器的子类,这取决于您使用库类的方式是否允许它工作。

If it's hard to tell, or they do modify things, then you need to use non-const instances and references to the library classes in your code. mutable can help with those of type (2), but for those of type (1) you just need to pass non-const arguments around.

For an example of why it might be hard, consider that the library author might have written something like this:

struct Foo {
    size_t times_accessed;
    int value;
    int get() {
        ++times_accessed;
        return value;
    }
};

Now, if you const_cast a const instance of Foo and call get(), you have undefined behavior[*]. So you have to be sure that get really doesn't modify the object it's called on. You could mitigate this a bit, by making sure that you never create any const instances of Foo, even though you do take const references to non-const instances. That way, when you const_cast and call get you at least don't cause UB. It might make your code confusing, that fields keep changing on objects that your functions claim not to modify.

[*] Why is it undefined behavior? It has to be, in order that the language can guarantee that the value of a const object never changes in a valid program. This guarantee allows the compiler to do useful things. For example it can put static const objects in read-only data sections, and it can optimize code using known values. It also means that a const integer object with a visible initializer is a compile-time constant, which the standard makes use of to let you use it as the size of an array, or a template argument. If it wasn't UB to modify a const object, then const objects wouldn't be constant, and these things wouldn't be possible:

#include <iostream>

struct Foo {
    int a;
    Foo(int a) : a(a) {}
};

void nobody_knows_what_this_does1(const int *p); // defined in another TU
void nobody_knows_what_this_does2(const int *p); // defined in another TU

int main() {
    const Foo f(1);
    Foo g(1);
    nobody_knows_what_this_does1(&f.a);
    nobody_knows_what_this_does2(&g.a);
    int x;
    if (std::cin >> x) {
        std::cout << (x / f.a); // Optimization opportunity!
        std::cout << (x / g.a); // Cannot optimize!
    }
}

Because f is a const object, and hence f.a is a const object, the optimizer knows that f.a has value 1 when it's used at the end of the function. It could, if it chose, optimize away the division. It doesn't know the same thing about g.a: g is not a const object, a pointer to it has been passed into unknown code, so its value might have changed. So if you're the author of nobody_knows_what_this_does1 or nobody_knows_what_this_does2, and you're thinking of const_casting p and using it to modify its referand, then you can only do it if you somehow know that the referand is non-const. Which normally you don't, so normally you don't use const_cast.

于 2012-05-22T16:30:31.590 回答
2

我认为您有以下选择:

  1. 如果您确定库在使用说明符时可以正常工作,则可以在处理库时const使用来删除对象的 const-nessconst_cast<>

    或者,您可以制作 const 对象的非常量副本并将其传递给库,然后更新对原始对象上非常量部分的更改

  2. 搜索另一个 const 正确的库

  3. 从您的代码中删除所有 const(不推荐)

于 2012-05-22T16:29:29.587 回答
2

Another option is to copy your object into a modifiable temp and pitch it. This is probably the safest thing to do if you're in the circumstance where your class offers a copy constructor and it's not too expensive. This has been my preferred method when it's available, as I know it's 100% safe. Silly example:

int getInfoFromString(String& str); //what??  why isn't str const :(

So I do

 String temp(str);
 int stuffINeed = getInfoFromString(temp);
 //happy
于 2012-05-22T16:33:28.203 回答
1

Another suggestion: Are you familiar with the mutable keyword? If you use it correctly, it might actually accomplish exactly what you're trying to do, with exactly one word added to your code. This keyword can evoke religious opinion at the level of goto because it's probably used as a kludge for every hundred times that it's used because it really is the best design option. In your case, it's debatable which it is but I think it fits the spirit: your annoying library object is something that can be fake-modified without breaking the semantic const-ness of your methods, so go ahead.

class Outer {
    mutable Inner inner;
    public void foo() const { 
        inner.nonConstMethod(); //compiles because inner is mutable
    }
};
于 2012-05-22T17:04:31.187 回答
1

If the library's interface is not big, you can create a wrapper, where you would adjust your code to expected types, by either casting or making a copy of parameters, which are passed to library's functions.

But do not degrade your code in order to use the library.

于 2012-05-22T19:16:56.880 回答