14-01-2022, 07:51 AM
Thanks for the reply. I realize how my example was unclear, let me show a couple more examples.
I think the main confusion with this rule is that "can be const" is ambiguous. What does it mean "can"?
Example 1: I want to implement a STL-like container class. To be consistent with what STL expects ("named requirements"), I need to implement:
These two functions are identical and they do not modify the internal state of the class. STL expects all container classes to have these 2 functions for read and write access. However, technically, one "can" add const to the first one, because the function is not modifying the internal state. It's just creating a copy of the internal pointer and returning it. The compiler will accept const and static analyzers will require const. However we will not be able to use this class because it doesn't fulfill the required expectations.
Example 2: this is a slightly off-topic from "interfaces enforced via templates" but still relevant I think; let me know if you'd rather have it in a separate post.
Here we have a setter, which everyone expects to be "non-const" as it's setting the internal state of the class. However, since it's a pointer, the compiler will accept adding "const". Technically, I "can" add const. But that doesn't mean I "should". Adding const will be against the principle of least surprise and will confuse developers. I'm aware of std::experimental::propagate_const, but naturally we are not going to use experimental features in a safety-critical environment.
Personally, I think it would be good to rephrase this rule in terms of "semantic const", instead of "syntactic const". Or at least clarify precisely what it's meant by "can be const".
Looking forward to your thoughts!
I think the main confusion with this rule is that "can be const" is ambiguous. What does it mean "can"?
Example 1: I want to implement a STL-like container class. To be consistent with what STL expects ("named requirements"), I need to implement:
Code:
T* data() { return data_; } // Violating M9-3-3?
const T* data() const { return data_; }
These two functions are identical and they do not modify the internal state of the class. STL expects all container classes to have these 2 functions for read and write access. However, technically, one "can" add const to the first one, because the function is not modifying the internal state. It's just creating a copy of the internal pointer and returning it. The compiler will accept const and static analyzers will require const. However we will not be able to use this class because it doesn't fulfill the required expectations.
Example 2: this is a slightly off-topic from "interfaces enforced via templates" but still relevant I think; let me know if you'd rather have it in a separate post.
Code:
class Foo
{
public:
~Foo() { delete x_; }
void set(int x) // Violates M9-3-3? Technically, I "can" add const and the code will compile
{
*x_ = x;
}
private:
int* x_{new int};
};
Here we have a setter, which everyone expects to be "non-const" as it's setting the internal state of the class. However, since it's a pointer, the compiler will accept adding "const". Technically, I "can" add const. But that doesn't mean I "should". Adding const will be against the principle of least surprise and will confuse developers. I'm aware of std::experimental::propagate_const, but naturally we are not going to use experimental features in a safety-critical environment.
Personally, I think it would be good to rephrase this rule in terms of "semantic const", instead of "syntactic const". Or at least clarify precisely what it's meant by "can be const".
Looking forward to your thoughts!