29

If I have a virtual function that carries an attribute

[[nodiscard]] virtual bool some_function() = 0;

Does that attribute get implicitly applied to overrides of that function?

bool some_function() override;

Or do I need the attribute again?

[[nodiscard]] bool some_function() override;
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218

2 Answers2

23

I sent an email to the C++ committee, specifically the Core working group, and provided the above example.

CoryKramer

It is currently unclear from the standard if attributes applied to virtual functions are inherited.

Response:

They are not. For them to be inherited, the Standard would have to explicitly say so, and it does not.

CoryKramer:

[After providing above code example] In the above example, I would expect both lines calling foo() to emit a compiler warning. I would hope that the attribute applies to all derived functions for consistency.

Response:

That's one perspective. Another is that, especially with covariant return types where the derived function returns a different type from that of the base function, it might very well be useful to make the base return type [[nodiscard]] but not the derived return type. There's currently no way to mark the derived function as not-[[nodiscard]].

More generally, it seems reasonable to get a different set of attributes when calling a derived function from those you get when calling the base function. If you know you have a derived class object, you have more specific information and behavior than if all you know is that it's a base class object, and attributes on member functions are part of that extra knowledge.

Reponses by Mike Miller of the C++ Core Working Group (3/30/2018).

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
14

I can't see any evidence in the C++17 wording that attributes are inherited by overriding functions.

The most relevant section I can find is the rules for overriding:

[class.virtual]/2: If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list (11.3.5), cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::vf. [..]

While this passage attacks the problem from a slightly different angle, I think it's enough to show that only virtualness is "inherited" (and that attributes don't come into play at all when deciding whether one function overrides another). That being said, I think this is slightly underspecified and could do with a clarifying note at the very least.

Of course, this quickly gets complicated. Given the below example:

struct B {
    [[nodiscard]] virtual bool foo() { return true; }
};

struct D : B {
    bool foo() override { return false; }
};

int main() {
    D().foo();
}

Clang will not issue a warning. However, access the function through a base pointer and it will.

struct B {
    [[nodiscard]] virtual bool foo() { return true; }
};

struct D : B {
    bool foo() override { return false; }
};

int main() {
    D d;
    ((B*)&d)->foo();
}

What that means for your question, I'm not sure.

Again, I'd like to see a bit more guidance from the standard on this topic.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • I sent an email to the committee after seeing the discussion and examples from this post, and pasted the response below. Not from the official standard, but it's at least straight from the horse's mouth (-ish). – Cory Kramer Mar 30 '18 at 20:15
  • @CoryKramer: Well, confirms my interpretation at least. Still think a non-normative note at least would have been appropriate. :) – Lightness Races in Orbit Mar 30 '18 at 20:17