Failures using 'std :: initializer_list' with 'const char *', 'const char * &' and 'std :: string'

3

I have a simple program that uses a variable template of type std::initializer_list that stores pairs that contain a type and a text:

using name_t = const char *;

template <typename key_t>
using key_name_t = std::pair<key_t, name_t>;

template <typename type_t>
std::initializer_list<key_name_t<type_t>> values;

The template variable is initialized by a variádica template:

template <typename type_t, typename ... KV>
void init(type_t value, const char *name, KV ... kv)
{
    values<type_t> = {{value, name}};
}

When using it:

int main()
{
    init(1, "uno", 2, "dos", 3, "tres");
    //             ~~~~~~~~~~~~~~~~~~~ <--- parámetros ignorados

    for (const auto &kv : values<int>)
        std::cout << kv.first << '\t' << kv.second << '\n';

    return 0;
}

I expected an exit like:

1    uno

But the output is random memory:

-809605168

I thought maybe the pointer within key_name_t is getting lost so I changed name_t :

using name_t = const char *const &;

Use constant reference to take advantage of the extension of the life cycle when associated with a constant reference, but then the code causes a segmentation fault , so I decide to change again name_t to alias of std::string and return to receive segmentation fault .

The complete code:

#include <iostream>

using name_t = 
               //const char *;
               //const char *const &;
               std::string;

template <typename key_t>
using key_name_t = std::pair<key_t, name_t>;

template <typename type_t>
std::initializer_list<key_name_t<type_t>> values;

template <typename type_t, typename ... KV>
void init(type_t value, const char *name, KV ... kv)
{
    values<type_t> = {{value, name}};
}

int main()
{
    init(1, "uno", 2, "dos", 3, "tres");

    for (const auto &kv : values<int>)
        std::cout << kv.first << '\t' << kv.second << '\n';

    return 0;
}

Available at Wandbox 三 へ (へ ਊ) へ ハ ッ ハ ッ .

  • Why am I receiving those segmentation fault ?
  • Why does the version with name_t = const char * point to invalid memory?
  • How can I get it working properly?
asked by PaperBirdMaster 17.04.2018 в 11:29
source

1 answer

1

According to the documentation :

  

Copying a std :: initializer_list does not copy the underlying objects.

So I do not think the allocation you make in init is going to be too productive. The reason? when you leave init the list is no longer valid because the data source has been deleted.

In fact if we do the following:

template <typename type_t, typename ... KV>
void init(type_t value, const char *name, KV ... kv)
{
    values<type_t> = {{value, name}};

    for (const auto &kv : values<int>)
        std::cout << kv.first << '\t' << kv.second << '\n';
}

We see as if it shows values ... but the original list, which is {{value, name}} (because we remember that at this point we are not initializing the global variable but making an assignment) will be destroyed.

Another example to control the life cycle:

struct T
{
  T() { std::cout << "T::T()\n"; }
  T(T const&){std::cout << "T::T(const&)\n"; }
  ~T() {std::cout << "T::~T()\n"; }
};

int main()
{
  std::cout << "Init()\n";
    init(T{}, "uno", T{}, "dos", T{}, "tres");
  std::cout << "Fin Init()\n";
  std::cout.flush();
    for (const auto &kv : values<int>)
        std::cout << /* kv.first << */ '\t' << kv.second << '\n';

  std::cout.flush();

  return 0;
}

Exit:

Init()
T::T()
T::T()
T::T()
T::T(const&)
T::~T()
T::~T()
T::~T()
T::~T()
Fin Init()

As many objects as have been created have been destroyed ... then the loop will access elements that are no longer alive.

Consider using another object type different from std::initializer_list

One possible solution:

#include <iostream>
#include <map>

using name_t = 
//const char *;
//const char *const &;
std::string;

template <typename key_t>
using key_name_t = std::map<key_t, name_t>;

template <typename type_t>
key_name_t<type_t> values;

template<class InsertIterator, class Type, class Value, class ... Args>
void FillContainer(InsertIterator iterator, Type type, Value value, Args... args)
{
  iterator = {type,value};

  if constexpr(sizeof...(args) > 1 )
    FillContainer(iterator,args...);
}

template <class type_t, typename ... KV>
void init(type_t value, const char *name, KV ... kv)
{
  FillContainer(std::inserter(values<type_t>,values<type_t>.end()),value,name,kv...);
}

int main()
{
    init(1, "uno", 2, "dos", 3, "tres");

    for (const auto &kv : values<int>)
        std::cout << kv.first << '\t' << kv.second << '\n';

    return 0;
}

You can see it working in Wandbox

    
answered by 17.04.2018 / 12:25
source