7

I'd like the following piece of code to work:

template <typename Self>
struct foo_base {
    auto get(typename Self::type n) { return n; }
};

template <typename T>
struct foo : public foo_base<foo<T>> {
    using type = T;
};

The problem of course is that the base is instantiated first so you cannot refer to the derived member types. I'd need some kind of lazy-evaluation here.

I've tried to make the function template and have SFINAE on it, something like:

template <typename Self>
struct foo_base {
    template <typename T, typename = std::enable_if_t<std::is_same_v<T, typename Self::type>>>
    auto get(T n) { return n; }
};

but it doesn't seem to affect the order. Any ideas?

Edit:

Constraints of solution:

  • I can't pass the type as a template parameter from the derived class. Main reason is: The type is very complicated to construct, several hundred characters. So can't do something like struct foo : foo_base<foo<T>, T> or variants.
  • I need to constraint the function to that type, I can't check inside the function. Maybe there are overloads in the derived class.
Peter Lenkefi
  • 1,306
  • 11
  • 29
  • Any particular reason why you can't just do `struct foo : foo_base, T>` or some variation of that? – Max Langhof Feb 18 '19 at 14:22
  • @MaxLanghof I'm making an extension mechanism. Technically I could do it, but in the real-world problem I need 2 types from the derived class, and both of them are pretty long if I have to construct them from template-parameters (way over 120 chars per type). – Peter Lenkefi Feb 18 '19 at 14:26
  • Basically, the _signatures_ of `foo_base`'s methods cannot depend on `Self` (and this includes any SFINAE). But you can do anything you want with `Self` in the bodies of `foo_base`'s methods: https://godbolt.org/z/eGgLt1. No idea if that helps in your case. The requirements of the `get` parameter type are pretty unclear from the question. – Max Langhof Feb 18 '19 at 14:38
  • Also, here is a variation where you don't have to repeat the `T` in the base type list, using template template parameters: https://godbolt.org/z/OOUtpf. If either of these satisfy you it would be great if you could update the question to clarify the constraints, so that a fitting answer can be given. – Max Langhof Feb 18 '19 at 14:41
  • @MaxLanghof Thank you for your input! I've updated the question of the constraints. – Peter Lenkefi Feb 18 '19 at 14:49

1 Answers1

4

You might create external traits, something like:

template <template T>
struct TypeT;

template <typename Self>
struct foo_base {
    auto get(typename TypeT<Self>::type n) { return n; }
};

template <typename T> struct foo;

template <template T>
struct TypeT<foo<T>> {
    using type = T; // Don't use foo<T>::type, so traits can be used with incomplete type
};

template <typename T>
struct foo : public foo_base<foo<T>> {
    using type = typename TypeT<foo>::type; // Or directly = T
};

Else you might indeed use SFINAE, but you must wait the type to be complete (when instantiating the method works in your case), as for example:

template <typename Self>
struct foo_base
{
    template <typename T = Self>
    auto get(typename T::type n) { return n; }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302