3

I have a class Point that inherits publicly from another class Vec3 and adds some data members. For example, when asking a spline for it's coordinate some distance along it the extra data returned could be the index of the nearest control point. A requirement is that users that don't care about the extra data in Point should be able to use it as-if it was a Vec3.

Point spline(double distance);
Vec3 position = spline(0.4);

This works as intended from the language specification point of view but we've recently incorporated clang-tidy and it gives the following warning:

error: slicing object from type 'Point' to 'Vec3' discards 4 bytes of state [cppcoreguidelines-slicing

Which is true and I want that check enabled in general, but is there some way to tell clang-tidy that slicing a Point to a Vec3 is ok?

I tried adding operator Vec3() to Point in hopes that it would pick that over slicing, but apparently conversion functions are never used when converting to a base class:

[class.conv.fct]
A conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified) same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to it), or to (possibly cv-qualified) void

class.conv.fct

Emphasis mine.

A small example:

struct Vec3 {
    double x, y, z;
};

struct Point : public Vec3 {
    int control;
    operator Vec3() { return Vec3 {x, y, z}; } // This does nothing.
};

Point spline(double distance)
{
    return Point {distance, 0.0, 0.0, 0};
};

int main()
{
    Vec3 point = spline(0.1);
}
main.cpp:7:5: error: conversion function converting 'Point' to its base class 'Vec3' will never be used [clang-diagnostic-warning,-warnings-as-errors]
    operator Vec3() { return Vec3 {x, y, z}; } // This does nothing.
    ^
main.cpp:17:18: error: slicing object from type 'Point' to 'Vec3' discards 4 bytes of state [cppcoreguidelines-slicing,-warnings-as-errors]
    Vec3 point = spline(0.1);

1 Answers1

2

This clang-tidy slicing flag is based on C++ Core Guidelines and they link to the exact section. Here you get the reasoning for the guideline as well as an alternative for when slicing is intended:

Alternative

If you mean to slice, define an explicit operation to do so. This saves readers from confusion.

In your example you could something like this:

struct Point : public Vec3 {
    int control;
    Vec3 copy_as_vec3() { return Vec3 {x, y, z}; }
};
bolov
  • 72,283
  • 15
  • 145
  • 224
  • 2
    Or `return *this; /* NOLINT(cppcoreguidelines-slicing) */`, which I think makes it even more obvious that the whole purpose is to perform slicing. – Brian Bi Jan 22 '20 at 20:43
  • @Brian I don't think the purpose is to slice. I think the purpose is to create a `vec3` out of a `Point`. Slicing happens to be the way to achieve this implementation. – bolov Jan 22 '20 at 20:45
  • While this goes against the intention of the original implementation (effortless/transparent mix-in into Vec3 expressions) I understand the rationale for the guideline. – Martin Nilsson Jan 22 '20 at 20:45
  • @Brian Where would I put `return *this;`? I have not been able to get any of my code to run on Point-to-Vec3 conversion since it's a base class. My current idea is to remove the inheritance, add a Vec3 member, and provide an `operator Vec3` conversion function. I think that will be compatible with all existing usage code and fix the warning. – Martin Nilsson Jan 22 '20 at 20:49
  • Member and `operator Vec3()` didn't work. I get hundreds or errors on code such as `Vec3 p = m_points[i] - m_points[i-1];` where `operator-` for two `Vec3` isn't considered when the two operands are both `Point`. – Martin Nilsson Jan 22 '20 at 21:08
  • The correct way is removing the copy constructor and letting the compiler move the data. look here https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c67-a-polymorphic-class-should-suppress-copying – Luiz Felipe Feb 02 '21 at 17:23
  • @LuizFelipe the guideline you link disables both copies and moves and does not answer how to slice when you need slicing behavior. – bolov Feb 02 '21 at 17:53
  • It doesn't copy, that's the new semantics after C++11. – Luiz Felipe Feb 22 '21 at 15:24