将变量模板化为字符串,而不是const char*

Template variable as string instead of const char *

本文关键字:const char 字符串 变量      更新时间:2023-10-16

我喜欢向量,通常在数组中使用它们。出于这个原因,我创建了一个模板化的变量函数来初始化向量(包含在下面)。

标题(.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

有没有什么方法可以让字符串参数被解释为字符串,而不必强制转换每个字符串参数?

因为常数"string one"的类型是const char*而不是std::string,所以需要进行转换。va_arg无法进行此转换,因此我们需要第二个模板参数:

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

为了好玩,我做了一个更整洁、更安全的小东西,但不会归纳成任意数量的争论。它通过过载工作。以下是前三个过载和示例用法:

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。

尝试使用两个模板类型参数:

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

通常(例如,对于intdouble等)TU将是相同的。但与新策略的不同之处在于,我们现在允许它们不同,前提是存在从UT的隐式转换(例如从const char*string)。这应该是安全的,因为如果不存在隐式转换,则会出现编译时错误。

顺便说一句,有趣的策略——我从未想过va_list等可以用这种方式!OTOH,我相信C++11中有一种新的机制,可以直接从初始化器列表中初始化向量等,类似于您一直能够在C中初始化数组(如int a[] = { 3, 4, 5 };),所以这样做可能更好。