【问题标题】:Template variable as string instead of const char *模板变量作为字符串而不是 const char *
【发布时间】:2012-08-21 19:37:41
【问题描述】:

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

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

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

【问题讨论】:

  • 这不是一个很好的解决方案,但也许我可以将“字符串”作为附加参数传递,然后自动转换为每个元素? ://
  • 告诉编译器你想要哪种类型而不是依赖演绎是否过于繁琐?以initVector&lt;std::string&gt;(2,"string 1","string 2") 为例。
  • 除了您的直接问题之外,将非 POD 类型作为可变参数传递是非法的。
  • @ahenderson:我相信错误消息不是由该行引起的,而是由于模板参数推导试图将类型参数 T 适合 2 个不同的类型 调用函数的点.
  • @bames53:您是对的,但这只是在将参数传递给函数之前尝试转换为string 的解决方案的问题。如果通过了原始const char*s(如我建议的解决方案),我们仍然很好。

标签: c++ string templates char


【解决方案1】:

因为常量"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

【讨论】:

  • 很棒的答案@nightcracker;这就像一个魅力。几个问题:1)我不知道这个 C++11 初始化程序列出了你和 j_random_hacker 指出的东西——我需要一个编译器选项来让它工作(它不是这样)。 2) 我一直选择不使用“sizeof(args)/sizeof(args[0])”方法,因为有人告诉我这样做是“不好的做法”……更不用说它有点不美观了。这是标准做法吗?
  • @zhermes:仍在寻找更好的答案。不过,您可以完美地使用数组答案,在此答案中使用 end 函数会更好:stackoverflow.com/a/4268956/565635。你用什么编译器?然后我可以给你旗帜。
  • 从技术上讲,参数“string one”不是const char*,而是const char [11](如果我计算正确的话),这个答案使问题过于复杂......如果你要将类型作为模板参数传递,不需要创建第二个类型并推导它,因为编译器能够convert从一种类型转换到另一种类型。
  • @nightcracker i686-apple-darwin11-llvm-g++-4.2(我认为这是您问题的正确答案......
  • @DavidRodríguez-dribeas: 是的,但在模板中使用时 const char[11] 会转换为 const char* - 我没有在意这些技术问题 :)
【解决方案2】:

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

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 };,所以最好去吧。

【讨论】:

  • 你甚至不需要从初始化列表中赋值。你可以使用新的初始化语法只写int a[]{ 3, 4, 5 };
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-19
  • 1970-01-01
相关资源
最近更新 更多