10

Is it possible to pass a template template paramater value by universal reference? Consider for example this minimal example for a function (not) working on STL sequences:

#include <iostream>
#include <vector>

template < template<typename,typename> class C, template<typename> class A, typename T >
void func(C<T, A<T>>&& c) {
    // usually I'd std::forward here, but let's just use cout...
    std::cout << c.size() << "\n";
}

int main (int argc, char const* argv[]) {
    func(std::vector<float>(2));
    std::vector<float> lv(3);
    func(lv);
}

It won't compile since the compiler doesn't know how to bind the l-value ("lv") in the second call to func. I'm a bit lost when it comes to the deduction rules for the type of C. Can anyone enlighten me?

Edit: Although I guess it is not relevant for the question: I used g++ 4.9 and clang 3.5 (both repo HEADs)

Richard Vock
  • 1,286
  • 10
  • 23
  • Changing for func(std::move(lv)); would work – Pedrom Jan 09 '14 at 14:24
  • I know, but that is because it is considered an r-value reference. I'd like it to be a universal reference as in template void func(C&&); – Richard Vock Jan 09 '14 at 14:25
  • From a design standpoint, this is a bizarre structural syntactic constraint. If your intent is to constrain `func` to work with parameters that conform to the STL's concept of `Sequence` you should probably do *that* instead of accepting any parameter that happens to have a template type with one particular structure. – Casey Jan 09 '14 at 16:49
  • The function I intended to use this for was meant to be an internal (not externalized) function. From a design standpoint I like to keep my internal functions as abstract as possible and type-constrain in the public interface... And as a side note: There are 2 questions in my OP and none have been answered (neither are they strictly speaking design related but related to the type deduction in C++11/14) – Richard Vock Jan 10 '14 at 18:54

1 Answers1

13

"Universal reference" is a colloquialism, and it always means, strictly, a reference T && where T is a deduced template parameter.

You can modify your code, though, to use just that:

template <typename T>
void func(T && c) { /* ... std::forward<T>(c) ... */ }

If you care that T is of the specified form, add a trait:

#include <type_traits>

template <typename T>
typename std::enable_if<IsATemplate<typename std::decay<T>::type>::value>::type
func(T && c) { /* ... std::forward<T>(c) ... */ }

Writing the IsATemplate trait is left as an exercise.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Hm... should still have a is_template check class somewhere in my codebase - just didn't thought about cheating myself around this problem. This works for me, so thanks and check+upvote is coming in a few minutes... – Richard Vock Jan 09 '14 at 14:31
  • 1
    One thing to note, the `IsATemplate` needs to handle the case where T is a reference, or you have to `std::remove_reference::type` to handle the lvalue case, since T will be deduced to `X&` in that case. – Dave S Jan 09 '14 at 14:42
  • @RichardVock: There are probably five different answers of myself on that topic on SO, along with countless others, in case you can't find your code :-) – Kerrek SB Jan 09 '14 at 14:45