60

This question on the object generator pattern got me thinking about ways to automate it.

Essentially, I want to automate the creation of functions like std::make_pair, std::bind1st and std::mem_fun so that instead of having to write a different function for each template class type, you could write a single variadic template template function that handles all cases at once. Usage of this function would be like:

make<std::pair>(1, 2);         // equivalent to std::make_pair(1, 2)
make<std::binder2nd>(&foo, 3); // equivalent to std::bind2nd(&foo, 3);

Is it possible to write this function make? I have tried this, but it doesn't work in GCC 4.5 or 4.6:

template <template <typename...> class TemplateClass, typename... Args>
TemplateClass<Args...> make(Args&&... args)
{
    return TemplateClass<Args...>(std::forward<Args>(args)...);
}

If I try to call (e.g) make<std::pair>(1, 2) I just get

error: no matching function for call to 'make(int, int)'

Have I got the syntax wrong anywhere here?
Or is this right and GCC is wrong?
Or is this just fundamentally impossible in C++0x?

[edit]

Proposal N2555 seems to suggest that this is allowed and GCC claims to have implemented it in GCC4.4.

Community
  • 1
  • 1
Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
  • 3
    I think there was a problem, that variadic template template parameters will only match variadic template classes, IIRC. Also, `mem_fn` and `bindNth` will be obsolete and deprecated with C++0x, thanks to `std::bind`. – Xeo Jun 26 '11 at 20:15
  • @Xeo: You mean a problem in GCC or the standard? N2555 suggests that it should be possible and GCC claims to have implemented it in 4.4 (see my edit) – Peter Alexander Jun 26 '11 at 20:19
  • 2
    @Peter: A proposal doesn't say it has been adopted into the FDIS (but I sure hope it has). Let me check that. **Edit**: The FDIS has the wording from the proposal, so I don't know where the problem lies. – Xeo Jun 26 '11 at 20:28
  • I'm wondering if there might be issues on how the template parameters are deduced? E.g., could it be that Args end up being reference types and this causes some SFINAE effect to discard your make function during resolution? – bluescarni Jun 26 '11 at 20:46
  • @bluescarni I can reproduce the error with `make(1, 2)` where `Args...` should be deduced as `int, int` with no reference types involved. – Luc Danton Jun 26 '11 at 20:54
  • I was trying to write my own solution but GCC 4.6 says "sorry, unimplemented: cannot expand ‘Args ...’ into a fixed-length argument list". Given the answers below, I will assume that GCC doens't support variadic templates correctly yet at this point. – Kerrek SB Jun 26 '11 at 20:56
  • @Kerrek I just discovered that with a variadic template template parameter (as in the OP) it is possible to beat this warning, at least with my snapshot of GCC – Luc Danton Jun 26 '11 at 21:00
  • @Luc: yeah, I just re-read the deduction rules and you're right. I'm still wondering if in a general case it still would work as expected or if it would be needed to sneak a std::decay in somewhere (at least in the case of std::pair). – bluescarni Jun 26 '11 at 21:01
  • @bluescarni At least for types like `std::pair` & `std::tuple` it would be customary to use `std::decay`, yes (and I recommend as such in my answer). However not using `std::decay` does not necessarily lead to incorrect code: getting a pair of references is what `std::tie` does (well, a tuple). Notice `std::tie(1, 2)` isn't allowed, though. – Luc Danton Jun 26 '11 at 21:06
  • @bluescarni A better example than `std::tie` would in fact be `std::forward_as_tuple`, which nets you a `std::tuple`. So perhaps returning `Template` is *still* not the thing to do ;) – Luc Danton Jun 26 '11 at 21:09
  • 5
    @PeterAlexander: As the answers mentioned, this seems a bug of current gcc. For your information, by the use of auxiliary(meaningless?) class template, [ideone(gcc-4.5.1)](http://ideone.com/ie7FV) compiled it somehow. – Ise Wisteria Jun 26 '11 at 21:34
  • Brilliant! I guess I'll add pretty recent GCC 4.7 to the list of GCC that it doesn't (directly) work with, but that will be fixed in due order. The inability to deduce type from constructor arguments is a huge language flaw IMHO. – Potatoswatter Jun 27 '11 at 05:38
  • See possibly related question [here](http://stackoverflow.com/questions/8514633/template-template-parameters-and-variadic-templates-with-gcc-4-4). – Faheem Mitha Dec 15 '11 at 03:20
  • @IseWisteria: You method fixes my error as well, which suggests it is the same bug. If this was your work, can you explain the motivation for this workaround? – Faheem Mitha Dec 15 '11 at 03:27
  • @PeterAlexander: Have you or anyone else reported this bug? – Faheem Mitha Dec 15 '11 at 03:28
  • @FaheemMitha: Honestly, it happened to work as a workaround. So, there wasn't technically interesting point in that code particularly(so I posted it as just a comment), sorry. – Ise Wisteria Dec 15 '11 at 18:37
  • @IseWisteria: It helped me. If you posted it as an answer, I for one would upvote it. – Faheem Mitha Dec 15 '11 at 18:47
  • @FaheemMitha: Thanks, glad it helped :-) , and thank you for your kind attention! That's enough to make me happy. – Ise Wisteria Dec 15 '11 at 20:08
  • Update: this works in `gcc 4.8.2` at least (`c++ -std=c++11`). – alfC Jan 03 '14 at 07:00

3 Answers3

53

That's exactly right. I would expect it to work. So I think that GCC is in error with rejecting that. FWIW:

#include <utility>

template <template <typename...> class TemplateClass, typename... Args>
TemplateClass<Args...> make(Args&&... args)
{
    return TemplateClass<Args...>(std::forward<Args>(args)...);
}

int main() {
  make<std::pair>(1, 2);
}


// [js@HOST2 cpp]$ clang++ -std=c++0x main1.cpp
// [js@HOST2 cpp]$
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
7

This is probably a GCC quirk. I can get the following to work with a dev snapshot (I don't have a copy of 4.6 right now):

template<
    template<typename...> class TemplateClass
    , typename... Args

    , typename Result = TemplateClass<Args...>
    // Also works with the arguably more correct
    // , typename Result = TemplateClass<
    //     typename std::decay<Args>::type...
    // >
>
Result
make(Args&&... args)
{ /* as before */ }
Luc Danton
  • 34,649
  • 6
  • 70
  • 114
4

This is quite wrong- take make_shared, for example. The point of make_shared is that there are run-time efficiency savings for using it. But what would happen if I tried to use make<std::shared_ptr>? Don't think that would quite work out. Or how about types where only some of the constructor arguments are template arguments, and the rest aren't? For example, make<std::vector, int>(other_vector.begin(), other_vector.end()); - the types of the iterators don't participate, but you pass them in anyway.

It's impossible to write a generic make function.

As for the Standard, well, it could easily have been removed since then. You'd have to check the FDIS.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • The point is not to make a function that constructs *any* class, just the ones like I described: i.e. ones whose template parameters come entirely from constructor arguments. – Peter Alexander Jun 26 '11 at 20:27
  • 1
    Theoretically, one could specialize the method for `shared_ptr`, but in general I agree this would cause more problems than it solves. – Dennis Zickefoose Jun 26 '11 at 20:32