Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CRTP for conditionally adding a member

Is it possible to write a CRTP class that adds a member to a class, only if a member with the same name does not exist (in C++17)?

template<class C> using require_foo = ...;

struct Foolish : require_foo<Foolish> {
    int foo;
}; // contains just one foo, CRTP is an empty base


struct NotFoolish : require_foo<NotFoolish> {
}; // contains just one foo, provided by CRTP base

I tried the obvious:

#include <type_traits>

void has_foo_helper(...);

template<class Class>
auto has_foo_helper(Class* obj) -> decltype(obj->foo);

template<class Class>
constexpr bool has_foo =
    std::is_same_v<decltype(has_foo_helper(std::declval<Class*>())), int>;

template<class, bool>
struct require_foo_impl;

template<class Class>
struct require_foo_impl<Class, false> {
};

template<class Class>
struct require_foo_impl<Class, true> {
    int foo;
};

template<class Class>
using require_foo = require_foo_impl<Class, has_foo<Class>>;

...but it doesn't work:

static_assert(has_foo<Foolish>); // failed due to requirement 'has_foo<Foolish>'
static_assert(has_foo<NotFoolish>); // failed due to requirement 'has_foo<NotFoolish>'

Thinking about it, has_foo seems paradoxical: if a class does not have a foo, it adds it, but now the class has a foo, so it does not add it, etc.

So I am pessimistic about the existence of a solution, but I'd love to be proven wrong.

like image 989
yorel Avatar asked Oct 21 '25 12:10

yorel


1 Answers

There is no solution here in C++, nor that it's likely that there will ever be one.

The fundamental issue is Foolish and NotFoolish's definitions are not complete until their closing brace, and they need to inherit from a superclass that needs to have a complete definition of the class that inherits from it. Without a complete definition it's not possible to determine, via CRTP, SFINAE, or any other technique, whether it has or has not a particular member. And the superclass must have a complete definition, of the superclass, before the definition of any class that inherits from it can be complete.

This chicken vs. egg problem is fundamentally unsolvable in C++.

like image 118
Sam Varshavchik Avatar answered Oct 23 '25 01:10

Sam Varshavchik



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!