7

私はベクトルが好きで、通常は配列よりもベクトルを使用します。そのため、ベクトルを初期化するためのテンプレート化された可変個引数関数を作成しました (以下に含まれています)。

ヘッダー (.h):

template <typename T>
vector<T> initVector(const int argCount, T first, ...);

ソース (.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;
}

ほとんどの型 (int、double など) ではうまく機能しますが、文字列では機能しません。コンパイラはそれらを 'const char *' として解釈するため、

vector<string> strvec = initVector(2, "string one", "string two");

エラーが表示されます:

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

それぞれをキャストすることなく、文字列引数を文字列として解釈する方法はありますか?

4

2 に答える 2

4

定数の型は"string one"isconst char*であり、ないstd::stringため、変換が必要です。va_argこの変換を行うことはできないため、2 番目のテンプレート引数が必要です。

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")

不要なオブジェクトが作成されないようにするために、resizeいくつかの変更を加えたことに注意してください。reserve


これを単純に使用することもできます (多くの要素が台無しになるリスクはなく、タイプセーフです):

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

または、C++11 初期化リストを使用します。

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

楽しみのために、私はこの小さなことを作成しました。これは、よりきちんとしていて安全ですが、任意の数の引数に一般化することはありません。オーバーロードすることで機能します。最初の 3 つのオーバーロードと使用例を次に示します。

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");

完全なヘッダー (最大 100 個の引数) は、 https ://gist.github.com/3419369 からダウンロードできます。

于 2012-08-21T19:42:09.400 に答える
1

代わりに 2 つのテンプレート タイプ パラメータを使用してみてください。

template <typename T, typename U>
vector<T> initVector(const int argCount, U first, ...) {

非常に頻繁に (例:など) int、同じになります。しかし、新しい戦略との違いは、 から への暗黙的な変換 ( from から など) があれば、それらを異なるものにすることができるようになったことです。これは、暗黙的な変換が存在しない場合、コンパイル時にエラーが発生するという意味で安全です。doubleTUUTconst char*string

va_listところで、興味深い戦略 -- etc をこのように使用できるとは思いもしませんでした! OTOH、C++ 11には、ベクトルなどをイニシャライザリストから直接初期化できる新しい機械があると思います。これは、Cのように常に配列を初期化できたのと同様int a[] = { 3, 4, 5 };です。 .

于 2012-08-21T19:50:35.560 に答える