6

I'm trying to use C++23's explicit object parameters (as introduced in P0847R7, "Deducing this") to replace the Curiously Recurring Template Pattern (CRTP) for a simple inheritance hierarchy, but I'm encountering a compiler error with Clang 20.1.2. The error suggests that the compiler cannot find a method (draw_impl) in the base class, even though it should be available in the derived class. This behavior contradicts examples in both the C++ proposal and a CppCon 2023 talk on non-virtual polymorphism, where explicit object parameters are advertised as a cleaner alternative to CRTP.

Here’s a minimal reproducible example:

#include <print>

struct shape {
    void draw(this auto&& self) {
        return self.draw_impl();  // Error: no member named 'draw_impl' in 'shape'
    }
};

struct triangle : public shape {
    void draw_impl() { 
        std::println("{}", "triangle");
    }
};

void draw_shapes(shape& type) {
    type.draw();
}

auto main() -> int {
    auto tri = triangle{};
    draw_shapes(tri);
    return 0;
}

When I compile this with Clang 20.1.2 and -std=c++2c (C++26 mode) using CMake, I get the following error:

main.cpp:5:16: error: no member named 'draw_impl' in 'shape'
    5 |                 return self.draw_impl();
      |                        ~~~~ ^
main.cpp:16:8: note: in instantiation of function template specialization 'shape::draw<shape &>' requested here
   16 |         type.draw();
      |              ^
1 error generated.

Questions:

  • Is this a limitation or bug in Clang 20.1.2’s implementation of C++26 explicit object parameters?
  • Did the design intent of explicit object parameters change between P0847R7 and C++26, making it unsuitable for direct CRTP replacement?
  • Does this work for others using different compilers (e.g., GCC, MSVC)? If so, what am I missing?

Environment:

  • Compiler: Clang 20.1.2
  • Standard: C++26 (-std=c++2c)
  • Build System: CMake

I’d appreciate any insights or workarounds. If this is expected behavior, could someone explain why and how to adjust my design to achieve the same goal as CRTP (i.e., allowing a base class to delegate to derived class methods without templates on the base)?

2
  • "Does this work for others using different compilers" - godbolt.org is your friend for comparing compilers. Commented Apr 6 at 17:39
  • "This behavior contradicts examples in both the C++ proposal and a CppCon 2023 talk on" - Which examples? All I see in the proposal is code that will work on the derived types statically, as will yours. But you are trying to make it work for the base class, as the error rightfully points out. The proposal clearly mentions templates as a motivating factor, so your non-template function isn't exactly inline with that. Commented Apr 6 at 18:22

2 Answers 2

9

Let's start with what the CRTP implementation would look like and back up from there.

With CRTP, you'd implement it this way:

template <class Derived>
struct shape {
    auto self() -> Derived& {
        return *static_cast<Derived*>(this);
    }

    void draw() {
        return self().draw_impl();
    }
};

struct triangle : public shape<triangle> {
    void draw_impl() { 
        std::println("{}", "triangle");
    }
};

template <class D>
void draw_shapes(shape<D>& type) {
    type.draw();
}

Note that draw_shapes would have to be a template.


Now, with deducing this, the interaction between shape and triangle gets simpler — as you already implemented. shape can just be a class, not a class template.

But draw_shapes doesn't change. It still has to be a template. It can't just take a shape&. So that could look something like this:

template <std::derived_from<shape> D>
void draw_shapes(D& type) {
    type.draw();
}

And now that works.


Conceptually, your implementation could not have worked. For draw_shapes to be a simple function that takes a shape& to be able to draw() different types — that's inherently asking for runtime polymorphism. Maybe that's a virtual function or some other kind of type erasure. But CRTP (and deducing this) are about static polymorphism.

In no way does your example "contradict" the proposal.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you so much for this. I definitely did not understand what static polymorphism meant in the context of explicit object parameters.
2

Deducing this doesn't and was never intended to deduce dynamic type of an object, it deduces static type. Clang is correct and other compilers would respond similarly. For doing what you want to do, draw_shapes has to be a template itself, maybe constrained by a concept checking that the type has appropriate interface.

5 Comments

If that is the case then this misconception is wide spread. I got this idea from A Journey into Non Virtual Polymorphism (Cppcon) where it was demonstrated. So much for slideware code
@AaronChifwaloShavesha could you please provide a timestamp for that code? Code from the slides of presentations sometimes contains typos, but this seems quite significant.
@AaronChifwaloShavesha I can see how the slides themselves could be misinterpreted (maybe the talk is clearer, idk), even though they don't contain wrong code per se, ie, in context, the only slide about deducing this could make it seem that it fixes the aforementioned requirement of using smth like std::variant or std::any for heterogenous storage and that you could use just shape, but it's wrong. You would still most likely use std::variant over derived types for something like this, irrespective of whether you implement them (and their base) using CRTP or deducing this or neither.
@AaronChifwaloShavesha That said, to avoid unnecessary runtime variant-dispatches when iterating over the elements (and make it cheaper to iterate over a particular shape type if needed) I'd use separate vectors for each derived type, and it's possible to do so generically using packs (e.g. std::vector-pack data member in some templated manager class).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.