Template: Force const parameter
-
Let's assume I have the following class:
class LibraryMimeData : public QMimeData { Q_OBJECT public: LibraryMimeData() = default; virtual ~LibraryMimeData() override = default; virtual QStringList formats() const; virtual bool hasFormat(const QString& mimeType) const; void setLibraryItems(QList<const LibraryItem*> libraryItems); QList<const LibraryItem*> libraryItems() const; template<class T> QList<T> libraryItems() const { QList<T> list; for (const LibraryItem* libraryItem : _libraryItems) { T item = dynamic_cast<T>(libraryItem); if (item) { list << item; } } return list; } private: Q_DISABLE_COPY(LibraryMimeData) QList<const LibraryItem*> _libraryItems; };
LibraryItem
is a base class with different subclasses. The class shown above is used to pass sets of those items around during drag'n'drop operations. The methodQList<T> LibraryItemMimeData::libraryItems() const
has been added for convenience to allow the receiver of the mime data to retrieve a list only containing items of a specific type. As all those "specific types" inherit from theLibraryItem
base class, I am just usingdynamic_cast
here (note:qobject_cast
wouldn't work becauseLibraryItem
doesn't inherit fromQObject
).My question is: Can I force the caller of that method to only use template types/parameters (the
T
thing) that are const pointers? Right now he could request a list ofQList<MyLibraryItemSubclass*>
. However, as theLibraryItemMimeData
class only has const pointers to those library items itself, it can only returnQList<const MyLibraryItemSubclass*>
.Any other comments regarding that piece of code are more than welcomed :)
-
I haven't tried it, but:
template<class T> QList<const T *> libraryItems() const { // ... QList<const T *> list; // ... }
?
-
The problem with that is that then the user/caller of that method has to write code like this:
QList<const MyLibraryItem*> myLibraryItems = mimeData->libraryItems<MyLibraryItem*>();
In my opinion that is just a no-go. You'd expect the two template parameters to be the same in that assignment.
-
@Joel-Bodenmann
Actually:QList<const MyLibraryItem *> myLibraryItems = mimeData->libraryItems<MyLibraryItem>();
But yes, they differ. Whether or not it's acceptable it's up to you, I know of no other way.
PS.
I think a fat notice in the docs should be just enough. This isn't c++11 so what's on the left can be different from what's on the right (i.e. there's noauto
confusing code). It's still valid to write:QWidget * button = new QToolButton();
-
Can I force the caller of that method to only use template types/parameters (the T thing) that are const pointers?
I'll just assume you really meant pointers to const, because const pointer would be
LibraryItem* const
, which is entirely different beast.This is one of those cases where templates come really handy. Here's a stripped down implementation:
class LibraryMimeData { private: QList<const LibraryItem*> _libraryItems; // this general type will get rid of non-pointer and pointer to non-const types template<typename T, bool is_pointer_to_const> struct Foo { static inline QList<T> convert(const QList<const LibraryItem*>& items) = delete; }; //the specialized type will only accept pointer to const types template<typename T> struct Foo<T, true> { static inline QList<T> convert(const QList<const LibraryItem*>& items) { return /* DO THE CONVERSION */; } }; public: //and here's a public interface template<typename T> QList<T> libraryItems() const { return Foo<T, std::is_pointer<T>::value && std::is_const<std::remove_pointer<T>::type>::value>::convert(_libraryItems); } };
This way it will work for pointer to const types and will give a lovely error about using deleted function for all others.
-
Ahahah, I needed a couple of reads to get it. It's quite nifty, albeit pretty verbose.
-
@kshegunov Yeah, templates usually do that when it's not you who wrote them ;) I usually steer away from them if I can but when you need to deal with type system and avoid runtime overhead they are a very flexible tool.
The verbose part is an implementation detail. Commented well and renamed to something better than Foo can live happily in a codebase. The user facing function has a nice clean signature. -
@Chris-Kawa said in Template: Force const parameter:
I usually steer away from them
Me too, which you can deduce from my simplistic answer :P
They're just marginally better than the preprocessor ... code writing code always perplexes me, I must admit ...PS.
I must also give you credit for the partial specialization trick ... never would've come up with it myself. -
They're just marginally better than the preprocessor
Awww, that's hurtful to the templates :) Preprocessor is just glorified find/replace machinery. Templates are a full blown, type safe, statically checked sub-language. Combined with constexpr they really have a Death-Star-like power (with similar caveats ;) )
I remember when I wrote template based compile time quick sort back in the days. Sure it resulted in a 500Mb executable and crashed the compiler for input arrays larger then a dozen elements but it was constant O(1) time for any input :P
Sorry for offtopic. Nostalgia took me over ;) -
@Chris-Kawa said in Template: Force const parameter:
I'll just assume you really meant pointers to const, because const pointer would be LibraryItem* const, which is entirely different beast.
Your assumption is correct. Sorry for using the wrong terminology. I'll blame not-getting-enough-sleep for it :p
@Chris-Kawa said in Template: Force const parameter:
Here's a stripped down implementation:
Oh boy... This is going to take a while (and lots of reading) for me to understand it. But I appreciate your efforts. Also, experience tells me that all code examples you write/post on this forum always just work, so huge Thank you! although I don't understand it yet :p
@Chris-Kawa said in Template: Force const parameter:
I remember when I wrote template based compile time quick sort back in the days. Sure it resulted in a 500Mb executable and crashed the compiler for input arrays larger then a dozen elements but it was constant O(1) time for any input :P
Is there any blog post / white paper you can link to? Sounds interesting :)
-
I guess the line that need the most explaining is this one:
return Foo<T, std::is_pointer<T>::value && std::is_const<std::remove_pointer<T>::type>::value>::convert(_libraryItems);
All the std magic is just a verbose way of saying "is T a pointer and does it point to a const value" so it's pretty much
Foo<T, true>::convert(_libraryItems)
for types that qualify and
Foo<T, false>::convert(_libraryItems)
for those that don't.Foo<T, false>
will call into thet deleted specialization andFoo<T, true>
into the real conversion.std::is_pointer<T>::value
- this istrue
for pointers,false
for others
std::remove_pointer<T>::type
- for any given pointer type it's a typedef for the pointed to type e.g forT
beingint*
it'sint
.
std::is_const<X>::value
- this istrue
for const types andfalse
for othersIs there any blog post / white paper you can link to? Sounds interesting :)
It was a class assignment over a decade ago. I'm sure the topic has a vast coverage, but I can't give you any links because it's simply useless in practice and I hadn't any interest in it after completing it.
-
I'm definitely in the wrong section of the forum given my knowledge of the language but why wouldn't a simple static_assert work here?
template<class T> QList<T> libraryItems() const { static_assert(std::is_pointer<T>::value && std::is_const<std::remove_pointer<T>::type>::value,"You can only use const pointers as template parameters"); QList<T> list; \\ etc
-
@VRonin said in Template: Force const parameter:
why wouldn't a simple static_assert work here?
It definitely would.