Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何正确地将C字符串传递给可变模板函数中的lambda_C++_C++11_Lambda_Variadic Templates_C Strings - Fatal编程技术网

C++ 如何正确地将C字符串传递给可变模板函数中的lambda

C++ 如何正确地将C字符串传递给可变模板函数中的lambda,c++,c++11,lambda,variadic-templates,c-strings,C++,C++11,Lambda,Variadic Templates,C Strings,以下是我的复杂情况: 我有一个使用可变模板和lambda的函数: template<typename...Args> void foo(Args...args) { // the arguments are passed to the lambda _func = [args...](){ do_sth(args...); }; } 给定我调用foo(pStr)

以下是我的复杂情况:

我有一个使用可变模板和lambda的函数:

template<typename...Args>
void foo(Args...args) {
    // the arguments are passed to the lambda
    _func = [args...](){ do_sth(args...); };
}
给定我调用
foo(pStr)\u func
时,pStr指向的临时字符串已被释放。我想知道是否有一个通用的方法来处理这个问题。我正在使用C++11

编辑:

也许我应该发布我的整个故事,正如你们中的许多人建议传递std::string而不是C字符串一样,有一些原因我无法逃避

我正在使用cocos2d-x开发游戏,它部署了C++11。我想做的是在玩家更改其语言偏好时支持标签的自动本地化(从UI中选择)

我将文本保存在几个文件中,每个文件都包含单一语言的本地化文本,它们基本上采用以下结构:

{
    "key1" : "_localized_text1_",
    "key2" : "_localized_text2_",
    ...
}
这样做的目的是观察语言偏好变化的事件(通过通知),我会从中获得一个指示该语言的键,以便从适当的文件中获取本地化文本。下面是我如何在对象类
标签中实现它的方法:

class Label {
    // this method would update the label's displayed text
    void setString(const std::string& text);

    // set a callback for changing language
    void setOnLanguageChangeFunc(std::function<void(Notification*)> func);

    // set a localised text, which would be updated on changing language
    void setLocalizeString(const std::string& key);
};
其中,
LOCALIZED_STRING
是使用键获取本地化字符串的宏帮助器;并且lambda
回调
将保存为
setOnLanguageChangeFunc
中的
Label
的本地成员变量

这在大多数情况下都非常有效,使情况变得复杂的是,本地化文本中涉及格式说明符,例如:

{
    ...
    "keyN" : "%s eats %d cookies",
    ...
}
此类格式占位符在代码中动态传递:

// formatStr = "Tom eats 5 cookies"
std::string formatStr = StringKit::stringWithFormat("%s eats %d cookies", "Tom", 5);
其中,
StringKit
是一个格式化字符串的实用程序,它接受可变参数,这些可变参数将被传递到
vsnprintf
以产生输出。现在您知道为什么我需要传递C字符串而不是std::string了,这只是因为格式化字符串的底层方法

现在我必须修改
Label::setLocalizeString
,以便它能够消化可能的可变参数:

template<typename... Args>
void setLocalizeString(const std::string& key, Args... args)
{
    // the callback lambda
    auto callback = [=](Notification *pNotification){
         setString(StringKit::stringWithFormat(LOCALIZED_STRING(sKey), args...)); 
    }

    // assign the lambda
    setOnLanguageChangeFunc(callback);
}
这种情况下,C字符串参数是全局的,但当它从一个临时std::string传递过来时,它的作用就像一个符咒:

std::string tempStr = "Tom";
pLabel->setLocalizeString("keyN", tempStr.c_str(), 5);
C字符串“Tom”在调用lambda回调时将丢失该值,因为指向的std::string已经消失


我尝试过几种方法,比如玩元组,或者在lambda中捕获一个基本类型的包装器类,但是没有一种方法能够解决这个问题。但是,我认为应该存在一些棘手的解决方案。

此问题与lambdas或可变函数无关-如果您只存储字符串,也会发生此问题:

const char* global_storage;

int main()
{
    {
        std::string s = "hi";
        global_storage = s.c_str();
    }

    // !!! `global_storage` points to deleted memory!
    use(global_storage);
}
您需要确保字符串的寿命足够长。使用
std::string
而不是
const char*
是一个很好的起点:

std::string global_storage;

int main()
{
    {
        std::string s = "hi";
        global_storage = std::move(s);
    }

    // OK, local string was moved into `global_storage`.
    use(global_storage.c_str());
}

如果您确实需要使用C样式字符串,只需将其作为
std::string
存储在lambda/which中,然后在需要使用时调用
.C_str()
,而不是在存储时调用它。

在lambda中存储时,需要将
char const*
参数转换为
std::string
。这是一种可能的方式,我可以提出:

#include <iostream>
#include <tuple>
using namespace std;

template<typename T, typename R = conditional_t<is_same<T, char const*>::value, string, T>>
R bar (T &&value) {return value;}

template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch,Tr>& os,
                      const Tuple & t,
                      std::index_sequence<Is...>)
{
    using swallow = int[]; // guaranties left to right order
    (void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...};
}

template<class Ch, class Tr, class... Args>
decltype(auto) operator<<(std::basic_ostream<Ch, Tr>& os,
                          const std::tuple<Args...>& t)
{
    os << "(";
    print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
    return os << ")";
}

template<typename...Args>
decltype(auto) foo(Args...args)
{
    return [args = make_tuple(bar(args)...)] () { cout<< args; return; };
}

int main() {
    string *s = new string("Hello, World!");
    const char *p = s->c_str();
    auto f = foo(1, p, 3.14);
    delete s;
    f();
    return 0;
}
#包括
#包括
使用名称空间std;
模板
R条(T&&value){返回值;}
模板
无效打印组(标准::基本组和操作系统),
常量元组&t,
std::索引(U序列)
{
使用swallow=int[];//保证从左到右的顺序

(void)吞下{0,(void(os-Well-duh)。不要使用C字符串。特别是当涉及所有权时。秘密成分:
std::string
[=]
。当您知道c++11已经支持
std::string
!您的用例绝不会取消
std::string
的资格。您将传递
std::string
的实例,并且在最后一个可能的时刻,当传递到
vsnprintf
时,您将
您的_string.c_str()
谢谢,但它需要每个char-const*参数才能有新的删除过程。这些代码将由除我之外的其他开发人员共享,因此我希望尽可能简单,我不能保证他们在处理内存时足够谨慎。无论如何,感谢您提供了一个解决方案,顺便说一句,我使用的是C++11如果字符串由于
std::string
中的小对象优化而变得足够短,则不会使用内存分配。尽管如此,如果您坚持在lambda中存储原始指针,我看不到任何解决问题的安全方法。@StephenChan:如果您与其他开发人员共享代码,其中的一个要求是保留它这比可能的要简单,您不应该一开始就使用lambdas。现在,如果您将lambdas与
std::string
副本一起使用,编译器可能会让您大吃一惊,并优化一个额外的分配。如果性能很重要,请始终进行测量(不要只是假设)@TobySpeight:接得好,我其实想在那里使用
c_str
const char* global_storage;

int main()
{
    {
        std::string s = "hi";
        global_storage = s.c_str();
    }

    // !!! `global_storage` points to deleted memory!
    use(global_storage);
}
std::string global_storage;

int main()
{
    {
        std::string s = "hi";
        global_storage = std::move(s);
    }

    // OK, local string was moved into `global_storage`.
    use(global_storage.c_str());
}
#include <iostream>
#include <tuple>
using namespace std;

template<typename T, typename R = conditional_t<is_same<T, char const*>::value, string, T>>
R bar (T &&value) {return value;}

template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch,Tr>& os,
                      const Tuple & t,
                      std::index_sequence<Is...>)
{
    using swallow = int[]; // guaranties left to right order
    (void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...};
}

template<class Ch, class Tr, class... Args>
decltype(auto) operator<<(std::basic_ostream<Ch, Tr>& os,
                          const std::tuple<Args...>& t)
{
    os << "(";
    print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
    return os << ")";
}

template<typename...Args>
decltype(auto) foo(Args...args)
{
    return [args = make_tuple(bar(args)...)] () { cout<< args; return; };
}

int main() {
    string *s = new string("Hello, World!");
    const char *p = s->c_str();
    auto f = foo(1, p, 3.14);
    delete s;
    f();
    return 0;
}