slashmais
slashmais

Reputation: 7165

Static variable in template function fail

Header file (test.h):

#ifndef _test_h_
#define _test_h_

#include <string>
#include <sstream>
#include <map>

typedef std::map<int, std::string> MIS;

//-----1-----
template<typename T> const std::pair<int, std::string> mkpair1(T t)
{
    static int i=0;
    std::stringstream ss;
    ss << t;
    return std::pair<int, std::string>(++i, ss.str());
}

template<typename...Any> void mkmap1(MIS &m, Any...any)
{
    m = { mkpair1(any)... };
};

//-----2-----
template<typename T> const std::pair<int, std::string> mkpair2(int i, T t)
{
    std::stringstream ss;
    ss << t;
    return std::pair<int, std::string>(i, ss.str());
}

template<typename...Any> void mkmap2(MIS &m, Any...any)
{
    static int i=0;
    m = { mkpair2(++i, any)... };
};

//-----3-----
template<typename T> const std::pair<int, std::string> mkpair3(int &i, T t)
{
    std::stringstream ss;
    ss << t;
    return std::pair<int, std::string>(++i, ss.str());
}

template<typename...Any> void mkmap3(MIS &m, Any...any)
{
    int i=0;
    m = { mkpair3(i, any)... };
};

#endif

Program file:

#include <iostream>
#include "test.h"

void ShowM(int x, const MIS &m)
{
    std::cout << "\n---" << x << "---\n";
    for (auto p:m) std::cout << p.first << " -> " << p.second << "\n";
}

int main(int argc, const char *argv[])
{
    MIS m;

    m.clear();
    mkmap1(m, 1, "two", 3.1415, "four", 5);
    ShowM(1, m);

    m.clear();
    mkmap2(m, 1, "two", 3.1415, "four", 5);
    ShowM(2, m);

    m.clear();
    mkmap3(m, 1, "two", 3.1415, "four", 5);
    ShowM(3, m);

    return 0;
}

Output:

---1---
1 -> 1
2 -> four

---2---
1 -> 1
2 -> two
3 -> 3.1415
4 -> four
5 -> 5

---3---
1 -> 1
2 -> two
3 -> 3.1415
4 -> four
5 -> 5

Why does the first template functions fail to build the map correctly? The result is strange. (Using gcc on Linux)

Edit:

Following on '101010' neat explanation, from cplusplus.com:

Because element keys in a map are unique, the insertion operation checks whether each inserted element has a key equivalent to the one of an element already in the container, and if so, the element is not inserted, returning an iterator to this existing element (if the function returns a value).

Upvotes: 2

Views: 79

Answers (1)

Dimitrios Bouzas
Dimitrios Bouzas

Reputation: 42939

As correctly @immibis mentioned in the comments, due to template argument deduction there are 3 template instantiations of template function mkpair1 generated, namely:

mkpair1<int>(...) // for inputs 1, 5
mkpair1<double>(...) // for input 3.1415
mkpair1<const char*>(...) // for input "tow" and "four"

Consequently, the variadic template expansion would look something like:

m = { mkpair1<int>(1), 
      mkpair1<const char*>("two"),
      mkpair1<double>(3.1415),
      mkpair1<const char*>("four"),
      mkpair1<int>(5)
    };

Now due to the fact that for each template instantiation generated you have a different static int i defined as well, you would have the following insertion scheme:

m.insert(std::make_pair(1, "1");      // OK
m.insert(std::make_pair(1, "two");    // ignored due to key 1 already exists
m.insert(std::make_pair(1, "3.1415"); // ignored due to key 1 already exists
m.insert(std::make_pair(2, "four");   // OK
m.insert(std::make_pair(2, "5"));     // ignored due to key 2 already exists

Thus rightfully, you get the result:

---1---
1 -> 1
2 -> four

Upvotes: 2

Related Questions