1

I was checking one of Qt's examples. In the example they have a simple TreeItem class to be placed on in a tree:

class TreeItem
{
public:
    explicit TreeItem(const QVector<QVariant> &data, TreeItem *parentItem = nullptr);
    ~TreeItem();

    void appendChild(TreeItem *child);

    TreeItem *child(int row);
    int childCount() const;
    int columnCount() const;
    QVariant data(int column) const;
    int row() const;
    TreeItem *parentItem();

private:
    QVector<TreeItem*> m_childItems;
    QVector<QVariant> m_itemData;
    TreeItem *m_parentItem;
};

In the implementation of the int row() const; method, they use a const_cast:

int TreeItem::row() const
{
    if (m_parentItem)
        return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));

    return 0;
}

To me, this smells like undefined behavior. What if this object is actually defined as const? But since this is in the official website as an example I want to know if I am right or not. At fist I thought maybe the intention is to never create a const TreeItem but then it would be pointless to mark the row method as const.
Question: Is it fine to use const_cast on this all the time as in the example?

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93

1 Answers1

3

This is not undefined behavior. const_cast is only undefined behavior if the object you start with is const and you modify the the object you get from the cast.

return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));

Is not going to modify const_cast<TreeItem*>(this) since indexOf takes a const T&.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Where in the standard it says only modifying is undefined behavior? – Aykhan Hagverdili Aug 20 '19 at 13:43
  • 1
    @Ayxan, "[..]Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior.[..]" (https://en.cppreference.com/w/cpp/language/const_cast) – vahancho Aug 20 '19 at 13:45
  • 2
    if `indexOf` takes a `const T&` the example is still questionable, because then the cast isnt need in the first place, or do I miss something? – 463035818_is_not_an_ai Aug 20 '19 at 13:46
  • @Ayxan if it was UB straight away, then there would not be any legal use case for `const_cast` – 463035818_is_not_an_ai Aug 20 '19 at 13:47
  • 1
    @formerlyknownas_463035818 It is needed because the `const` of `indexOf` applies to the pointer, not what it points to. You need `const_cast` so `this` becomes a `TreeItem*` and then `indexOf` makes that a `TreeItem* const` and finds it. – NathanOliver Aug 20 '19 at 13:48
  • @formerlyknownas_463035818 `int i; const int* cp = &i; int *p = const_cast(cp)` This seems legal since `i` is not const. But `this` may be referring to a const object – Aykhan Hagverdili Aug 20 '19 at 13:50
  • @Ayxan As long as the result isn't used to modify the object, it's fine to cast `const` away. The only thing you can't do is modify an object that is actually `const`. Edit : By fine, I mean legal. – François Andrieux Aug 20 '19 at 13:53
  • 1
    @Ayxan The rules for `const_cast` is [here](https://timsong-cpp.github.io/cppwp/expr.const.cast). There is nothing in there saying you can't cast const away only if the source is non const. That means the only rule we have to worry about is the rule that modifying an actual const object is UB. Since we aren't modifying, there is no UB. – NathanOliver Aug 20 '19 at 13:53
  • @Ayxan Here is a SO Q&A about it: https://stackoverflow.com/questions/29883327/is-it-safe-to-remove-const-via-const-cast-and-invoke-a-non-const-function-that-d – NathanOliver Aug 20 '19 at 13:57