1

I had this code working in VS2012 Nov CTP:

//.h
template<template <typename...> class Container>
class Test {
    typedef Container<unsigned int, TestEntry> L1;
    Test();
    ...
}

//.cpp
template<template <typename...> class Container>    
Test<Container>::Test() {}
...
template class Test<std::map>;
template class Test<std::unordered_map>;

//main.cpp
#include "test.h"
#include <map>
#include <unordered_map>
int main()
{
    Test<std::map> test;
    std::cout << "COMPILES!!!" << std::endl;
    return 0;
}

I just updated to Visual Studio 2013 Ultimate and it will not compile, with the error:

'std::map' : too few template arguments

I know that I can relax the template requirements with something like:

template< typename MapType>

But this is not a good solution for me, as the only thing that I want to customize is the container type, not the contents. Also the contents are complex and would be a problem to have to write each time.

Is there a way to solve this in VS2013? I have been trying to fix it for hours with no luck.

Update:

To reproduce exactly in VS2013:

//test.h
#include <map>
#include <unordered_map>
template<template <typename...> class Container>
class Test {
    typedef Container<unsigned int, unsigned int> L1;
};

//test.cpp
#include "test.h"
template<> class Test<std::map> {
public:
    typedef std::map<unsigned int, unsigned int> L1;
};
template<> class Test<std::unordered_map> {
public:
    typedef std::unordered_map<unsigned int, unsigned int> L1;
};

//main.cpp
#include "test.h"
#include <iostream>
#include <cmath>
#include <map>
#include <unordered_map>
int main()
{
    Test<std::map> test3;
    std::cout << "COMPILES!!!" << std::endl;
    return 0;
}
Luis
  • 678
  • 8
  • 14

2 Answers2

2

You are missing <> in specializations:

template<> class Test<std::map>
{
...
};
template<> class Test<std::unordered_map>
{
...
};

Update:

A full specialization is introduced with a sequence of three tokens: template, < and >. The same prefix is also needed to declare full function template specializations. Earlier designs of the C++ language did not include this prefix, but the addition of member templates required additional syntax to disambiguate complex specializations. [C++ Templates, Vandervoorde et al. page 190].

101010
  • 41,839
  • 11
  • 94
  • 168
  • Yep! You are right, thanks a lot. I will accept your answer, but could you elaborate a little bit on your answer. I don't understand why the <> is needed. – Luis May 13 '14 at 08:48
  • Thanks for the update, now I understand better. But still I am having problems compiling now outside of the templated classes, e.g: Test test(); fails to compile with the same error. – Luis May 13 '14 at 09:09
  • What kind of problems? – 101010 May 13 '14 at 09:10
  • Using the templated class, although I may be wrong; I used to call it like this: Test test(); Now it does not compile: 'std::map' : too few template arguments – Luis May 13 '14 at 09:12
  • @Luis You need to provide bodies for you specializations (i.e., `template<> class Test { ... };`) the code quoted is is just for abbreviation. – 101010 May 13 '14 at 09:14
  • How would you go about the body, because it will always have too few arguments, I have tried: `template<> class Test{ public: typedef std::map L1; };` And it wont't compile either, too few arguments when using `Test test(); ` – Luis May 13 '14 at 09:38
  • @Luis compiles fine to me in VC++2013 and GCC 4.8. What compiler are you using? See http://coliru.stacked-crooked.com/a/dfd8849824872407 – 101010 May 13 '14 at 09:47
  • I'm using VS2013 compiler and yes you are right, using it from the body of a function works. But, when I try to expose from Test2 class again the choice, its when it does not compile: `template<> class Test2 { public: Test test; };` This is when I see too few arguments for std::map error again. See: [link](http://coliru.stacked-crooked.com/a/16b45a8621845130) – Luis May 13 '14 at 10:06
  • @Luis I don't know dude, I have VC++2013 and your new example compiles as well. – 101010 May 13 '14 at 10:30
  • I think it has to do with calling it from classes in different .cpps and .hs. When you compile it all in the same file it worked, you are right in your last post. But for example including Test from another file it stops working and errors with template has too few arguments. I should specify the class is part of a library, that is used from an external project. – Luis May 13 '14 at 10:57
  • I have added the exact code that reproduces the failure to compile in VS in my question. @40two – Luis May 13 '14 at 11:11
  • @Luis it compiles to different files as well. What error are you getting? – 101010 May 13 '14 at 11:24
  • test.h(8): error C2976: 'std::map' : too few template arguments – Luis May 13 '14 at 11:25
  • @Luis http://coliru.stacked-crooked.com/a/22522b3b255875a1 It works on seperate files on VC++2013 as well. – 101010 May 13 '14 at 12:40
  • I think I must be going crazy, your code with Visual Studio 2013 (v120); having test.h, test.cpp and main.cpp for me does not compile. This is the console output `test.cpp main.cpp test3.h(17): error C2976: 'std::map' : too few template arguments map(69) : see declaration of 'std::map' main.cpp(63) :see reference to class template instantiation 'Test' test.h(17): error C2976: 'std::unordered_map' : too few template arguments unordered_map(79) : see declaration of 'std::unordered_map' main.cpp(65) : see reference to class template instantiation 'Test'` – Luis May 13 '14 at 15:05
  • Ok, I think I made some progress. I got it to compile putting everythig on test.h, but I am not sure if I can do what I wanted. What I wanted to do is keep the functions of the class, and just use a map or hashmap depending on what the user wants. So, in short, I want to just change the container, without reimplementing functions that will have exactly the same code. Am I explaining myself correctly? Can it be done separating the code of declaration in .h and implementation of the functions in .cpp? – Luis May 13 '14 at 15:44
  • Take a look at http://stackoverflow.com/questions/15367369/what-is-a-robust-way-of-template-specialization-in-c-for-separated-header-sour. It's OK to put them all in the same header file (in fact the class specialization definitions have to be in the same file). – 101010 May 13 '14 at 19:04
1

One way is:

#include <iostream>
#include <map>
#include <vector>
#include <unordered_map>

template<template <typename...> class Container, typename ... T>
class Test
{
    public:
    typedef Container<T...> container_type;
    static void print() { std::cout << "General\n"; }
};

template<typename ... T>
class Test<std::map, T...>
{
    public:
    typedef std::map<T...> container_type;
    static void print() { std::cout << "Map\n"; }
};

template<typename ... T>
class Test<std::unordered_map, T...>
{
    public:
    typedef std::unordered_map<T...> container_type;
    static void print() { std::cout << "Unordered Map\n"; }
};


int main() {
    Test<std::vector, int>::print();
    Test<std::map, int, int>::print();
    Test<std::unordered_map, int, int>::print();
}
  • Can functions be shared between std::map version and std::unordered_map? For example a find function would be exactly the same in both especializations. Can you separate this in .h and .cpp? – Luis May 13 '14 at 15:49
  • @Luis No (these are two distinct templates, which will generate two sets of code. Although, you might forward to s single template (taking a map or unordered_map) in your source file. You might look up extern templates (http://stackoverflow.com/questions/8130602/using-extern-template-c0x). –  May 13 '14 at 16:21
  • I don't think I can use extern, as I am using __declspec(dllexport) for exporting my library. Could you provide a small example using extern just in case? – Luis May 13 '14 at 16:47
  • I also don't understand what you mean when you say "Although, you might forward to s single template (taking a map or unordered_map) in your source file" could you explain further? – Luis May 13 '14 at 17:10