Template variable as string instead of const char *

c++, char, string, templates

Solution

Because the type of the constant `"string one"` is `const char*` and not `std::string` there needs to be a conversion. `va_arg` can not make this conversion, so we need a second template argument:

template <typename VecT, typename EleT>
std::vector<VecT> init_vector(const size_t nargs, EleT first, ...) {
    std::vector<VecT> result;
    result.reserve(nargs);

    if (nargs == 0) {
        return result;
    }

    result.push_back(first);

    if (nargs == 1) {
        return result;
    }

    va_list valist;
    va_start(valist, first);

    for (int i = 1; i < nargs; ++i) {
        result.push_back(VecT(va_arg(valist, EleT)));
    }

    va_end(valist);

    return result;
}

std::vector<std::string> = init_vector<std::string>(2, "string one", "string two")

Note that I made some changes, most notably change `resize` to `reserve`, to prevent unnecessary objects from being created.

You can also simply use this (no risk of having number of elements messed up, and type safe):

const char *args[] = {"string one" , "string two"};
std::vector<std::string> strvec(args, args + sizeof(args)/sizeof(args[0]))

Or use C++11 initializer lists:

std::vector<std::string> strvec = {"string one" , "string two"};

For fun I made this little thing that is even neater and safer, but doesn't generalize into an arbitrary amount of arguments. It works by overloading. Here are the first three overloads and example usage:

template<class C>
inline C init_container() {
    return C();
}

template<class C, class T>
inline C init_container(T arg0) {
    const T args[1] = {arg0};
    return C(args, args + 1);
}

template<class C, class T>
inline C init_container(T arg0, T arg1) {
    const T args[2] = {arg0, arg1};
    return C(args, args + 2);
}

std::vector<std::string> vec =
    init_container< std::vector<std::string> >("hello", "world");

A full header (for up to 100 arguments) can be downloaded here: https://gist.github.com/3419369 .

Problem

I like vectors, and generally use them over arrays. For that reason I created a templated variadic function to initialize vectors (included below). Header (.h): ``` template <typename T> vector<T> initVector(const int argCount, T first, ...); ``` Source (.hpp): ``` template <typename T> vector<T> initVector(const int argCount, T first, ...) { vector<T> retVec; retVec.resize(argCount); if(argCount < 1) { ... } retVec[0] = first; va_list valist; va_start(valist, first); for(int i = 0; i < argCount-1; i++) { retVec[i+1] = va_arg(valist, T); } va_end(valist); return retVec; } ``` It works great for most types (e.g. int, double...), but not for strings---as the compiler interprets them as 'const char *', thus ``` vector<string> strvec = initVector(2, "string one", "string two"); ``` gives me the error: ``` error: conversion from ‘std::vector<const char*, std::allocator<const char*> >’ to non-scalar type ‘std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >’ requested ``` Is there any way to get the string arguments to be interpreted as strings without having to cast each one?

Original source

Related problems