5

I'm exploring template shenanigans in C++ (C++11), and one thing I'd like to have is a pure virtual type in an abstract class. This would be like Scala's abstract types. In C++ I'd want to do something like the following:

struct Base {
  // Says any concrete subclass must define Type, but doesn't
  // require that it be anything in particular.
  virtual typedef MyType; 
};

struct Derived : Base {
  // Won't compile unless this typedef exists.
  typedef int MyType;
};

Any idea how to do this?

Xeo
  • 129,499
  • 52
  • 291
  • 397
emchristiansen
  • 3,550
  • 3
  • 26
  • 40
  • 2
    Why do you need this? – Xeo Mar 04 '13 at 19:46
  • 1
    What do you want this for? Knowing your goal will certainly make it better to get you an answer. And no, "emulating some random Scala feature" does not count as a goal. – R. Martinho Fernandes Mar 04 '13 at 19:46
  • @Xeo - The poster is "exploring template shenanigans" - it's a contrived goal, but still a goal! :) –  Mar 04 '13 at 20:08
  • I'm implementing typeclasses in C++. (I know this has been done before, sorta, but nothing I've found is quite what I want.) In particular, if I want to write a generic typeclass that supports mapping, I need something like Scala's CanBuildFrom (http://www.scala-lang.org/api/current/index.html#scala.collection.generic.CanBuildFrom). Doing this nicely seems to require virtual types. – emchristiansen Mar 04 '13 at 20:08

2 Answers2

5

I am not sure there is a real need for this in C++.

Trying to put myself in the position of a designer who is looking for such a solution, I would say this kind of constraint would be needed to enforce some types to adhere to some syntactic convention.

Most likely, this is needed because a generic algorithm requires that syntactic convention: it cannot work with types that do not define such a type association.

For instance, the algorithm below requires the type of its argument to have an associated bar_type:

template<typename T>
bool foo(T t)
{
    typedef typename T::bar_type FT;
    FT ft;
    ...
}

But if this is the case, there is no need for enforcing a typedef to effectively constraint the input of foo<>(): simply omitting a type definition for bar_type won't make it possible to use that type with foo<>().

Of course, you would discover this only as soon as you actually try to do so, and not before. And being able to define a concept such as HasBarType, and then to enforce some types to realize that concept would be nice; on the other hand, concepts are not yet part of C++ and, as much as they are desirable, it is possible to live without them.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • There's also `my_tag_type::bar_type` if you want to be able to make existing 3rd party or primitive types fullfill your requirements. See `std::iterator_traits::value_type`. – Mooing Duck Mar 04 '13 at 20:00
  • @MooingDuck: Right, that's another way of enforcing it – Andy Prowl Mar 04 '13 at 20:02
  • @AndyProwl, you've hit the nail on the head re Concepts; I'm trying to force early compilation failure if a typeclass (basically a Concept, as I understand it) is not properly implemented. – emchristiansen Mar 04 '13 at 20:18
  • @emchristiansen: Then I'm not sure if this is feasible with a "once and for all" declaration/assertion. In C++, I believe you still have to specify one declaration/assertion for each type explicitly: it could be *outside* those types, but I would say it must be present. – Andy Prowl Mar 04 '13 at 20:21
1

edit

This doesn't work, but I think the curiously recurring template pattern might be the way to go.

/edit

template<typename Dependent>
class Base : public Dependent {
    typedef typename Dependent::MyType MyType;
};

Then use the curiously recurring template pattern:

struct Derived : Base<Derived> {
  // Won't compile unless this typedef exists.
  typedef int MyType;
};
Community
  • 1
  • 1
Peter Wood
  • 23,859
  • 5
  • 60
  • 99
  • [Have you tried compiling that](http://liveworkspace.org/code/2x2bEe$8)? Btw, missing `typename` :-) – Andy Prowl Mar 04 '13 at 19:58
  • @PeterWood, I've tried to solve this generally with the CRTP, without success. The apparent problem: CRTP adds dependencies from Base to Derived, and when Derived has dependencies on Base you can get an infinite template loop. – emchristiansen Mar 04 '13 at 20:22