为什么我的字符串连接符在当前字符串的长度与前一个字符串的长度相同时发出前一个字符串而不是当前字符串

时间:2015-08-12 17:21:16

标签: c++ templates

所以,我跟随花式字符串连接器:

#include <iostream>
#include <sstream>

class Internal final
{
    ~Internal() = delete;
    static std::stringstream stream;
    static const std::ios_base::fmtflags defflags;
    template <typename T, typename ...P> struct Append
    {
        static void func(const void *const *p)
        {
            stream << *(T *)*p;
            Append<P...>::func(p + 1);
        }
    };
    template <typename T> struct Append<T>
    {
        static void func(const void *const *p)
        {
            stream << *(T *)*p;
        }
    };
    template <typename ...P> friend const char *Jo(const P &... p);
};

std::stringstream Internal::stream;
const std::ios_base::fmtflags Internal::defflags = stream.flags();

template <typename ...P> const char *Jo(const P &... p) // 'Join', returned pointer is valid until next call
{
    Internal::stream.clear();
    Internal::stream.flags(Internal::defflags);
    Internal::stream.str("");
    const void *const arr[sizeof...(P)] {&p...};
    Internal::Append<P...>::func(arr);
    static std::string ret = Internal::stream.str();
    return ret.c_str();
}


int main()
{
    // Test case:
    std::cout << Jo("Hello",',',' ',"world!!", 1); // Prints `Hello, world!!1`
    return 0;
}

如您所见,Jo(...)连接所有传递的对象并返回指向结果字符串的指针。这些对象可以包含stringstream接受的所有类型。此外,此函数接受流操纵器并在每次调用时重置内部stringsteram标志。

它工作正常,但我有一个非常奇怪的问题。

请考虑以下代码:

std::cout << Jo('1','2');
std::cout << Jo('3','4');

我希望它能打印1234,但会打印1212

当我连续多次使用我的函数时,总是会发生同样的事情,当每个结果字符串具有相同的长度时。在这些情况下,它始终按顺序打印第一个字符串。

See it live.

有谁知道为什么会这样?

P.S。此问题与std::cout优化无关,我在没有std::cout的情况下对其进行了测试。

1 个答案:

答案 0 :(得分:3)

static std::string ret = Internal::stream.str();

...只执行一次,因为它是static变量的初始值设定项。只需将其删除,然后从std::string返回Internal::stream.str()

如果你想获取灵感,这是Jo的版本。我保留了静态stringstream,但它使Jo的所有实例化都很常见,但我不确定它是否有意义共享它(线程安全问题等)。我会把旗帜扯到你身上;)

namespace Jo_detail {
    std::stringstream stream;
}

template <class... Args>
std::string Jo(Args &&... args) {

    Jo_detail::stream.str("");

    // Classic expander trick with dummy array
    using ex = int[];
    (void) ex { 0, (void(Jo_detail::stream << std::forward<Args>(args)), 0)... };

    // str() returns a copy anyway
    return Jo_detail::stream.str();
}