可接受rvalue和左值引用的变量模板?

时间:2013-10-28 00:30:13

标签: c++11 variadic-templates rvalue-reference typetraits lvalue

我想编写一个接受rvalues和lvalue引用的可变参数模板函数。它将大写std :: strings,并在大写后显示每个参数。在函数结束后,所有左值都应保持大写(即通过引用传递左值) 例如,我想要这种行为:

std::string hello = "hello";
std::string planet = "planet";
std::string earth = "earth";

//the order and amount of rvalues and lvalue references, should not matter
Capitalize_And_Output("hello","planet","earth"); //outputs: "HELLO PLANET EARTH"
Capitalize_And_Output(hello,"planet","earth");   //outputs: "HELLO PLANET EARTH"
Capitalize_And_Output("hello",planet,"earth");   //outputs: "HELLO PLANET EARTH"
Capitalize_And_Output("hello","planet",earth);   //outputs: "HELLO PLANET EARTH"
Capitalize_And_Output(hello,planet,"earth");     //outputs: "HELLO PLANET EARTH"
Capitalize_And_Output(hello,"planet",earth);     //outputs: "HELLO PLANET EARTH"
Capitalize_And_Output("hello",planet,earth);     //outputs: "HELLO PLANET EARTH"
Capitalize_And_Output(hello,planet,earth);       //outputs: "HELLO PLANET EARTH"

//lvalue references remain changed after the function call
std::cout << hello << std::endl;  //outputs: "HELLO"
std::cout << planet << std::endl; //outputs: "PLANET"
std::cout << earth << std::endl;  //outputs: "WORLD"

如何将上面这段代码编译成如图所示进行编译和工作?

到目前为止,我能够输出信息,但我不知道如何处理两种不同值类型的大小写。以下代码编译,因为我已注释掉不起作用的行。

#include <string>
#include <iostream>
#include <algorithm>

template<typename T>
void Capitalize_And_Output(T & str) {
    //std::transform(str.begin(), str.end(), str.begin(), ::toupper); <- will not compile
    std::cout << str<< std::endl;
    return;
}

template<typename First, typename ... Strings>
void Capitalize_And_Output(First & str, const Strings&... rest) {
    //std::transform(str.begin(), str.end(), str.begin(), ::toupper); <- will not compile
    std::cout << str << " ";
    Capitalize_And_Output(rest...);
    return;
}

int main() {

    std::string hello = "hello";
    std::string planet = "planet";
    std::string earth = "earth";

    //the order and amount of rvalues and lvalue references, should not matter
    Capitalize_And_Output("hello","planet","earth"); //outputs: "HELLO PLANET EARTH"
    Capitalize_And_Output(hello,"planet","earth");   //outputs: "HELLO PLANET EARTH"
    Capitalize_And_Output("hello",planet,"earth");   //outputs: "HELLO PLANET EARTH"
    Capitalize_And_Output("hello","planet",earth);   //outputs: "HELLO PLANET EARTH"
    Capitalize_And_Output(hello,planet,"earth");     //outputs: "HELLO PLANET EARTH"
    Capitalize_And_Output(hello,"planet",earth);     //outputs: "HELLO PLANET EARTH"
    Capitalize_And_Output("hello",planet,earth);     //outputs: "HELLO PLANET EARTH"
    Capitalize_And_Output(hello,planet,earth);       //outputs: "HELLO PLANET EARTH"

    //lvalue references keep changed value after the function call
    std::cout << hello << std::endl;  //outputs: "HELLO"
    std::cout << planet << std::endl; //outputs: "PLANET"
    std::cout << earth << std::endl;  //outputs: "WORLD"

    return 0;
}

我怎样才能让它发挥作用? 也许转换函数不起作用,因为rvalues实际上是一个不同的类型?他们是char * s?

我的想法是什么:
我是否必须对类型特征做些什么? 有R值参考的东西?
有普遍崇敬的东西(不是真的确定那是什么)?

请更正任何滥用术语的行为!

2 个答案:

答案 0 :(得分:1)

http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11

了解通用参考资料。这是一个更好的方式(恕我直言)思考这些事情,我觉得他的视频一般很好地解释了右值参考。

答案 1 :(得分:1)

首先,程序不起作用的根本原因不仅与rvalue / lvalue兼容性有关。为了表明考虑这个只通过值传递参数的版本

#include <string>
#include <iostream>
#include <algorithm>

template<typename T>
void Capitalize_And_Output(T str) {
    std::transform(str.begin(), str.end(), str.begin(), ::toupper); 
    std::cout << str<< std::endl;
    return;
}

template<typename First, typename ... Strings>
void Capitalize_And_Output(First str, Strings... rest) {
    std::transform(str.begin(), str.end(), str.begin(), ::toupper); 
    std::cout << str << " ";
    Capitalize_And_Output(rest...);
    return;
}

在这种情况下,您的主要代码也无效。要表明考虑以下

int main() {    
    std::string hello = "hello";
    std::string planet = "planet";
    std::string earth = "earth";
    Capitalize_And_Output(std::string("hello"),planet,earth);  // this will work
    //Capitalize_And_Output("hello",planet,earth);  // original code -> this will NOT work
    return 0;
}

该行

Capitalize_And_Output("hello",planet,earth);

不起作用,因为编译器认为第一个参数是“一个const char”(char没有.begin()和.end()迭代器!)!.实际上,我在g++

中收到以下错误消息
instantiated from here
teste.cpp:14:5: error: request for member ‘begin’ in ‘str’, which is of non-class type ‘const char*’

现在回到原来的左值/右值问题(折叠规则说&amp;&amp;&amp; =&amp;。这是解决方案。你也不能忘记“完美地向前”rvalue引用。

然后,最终答案的摘要是

template<typename T>
void Capitalize_And_Output(T&& str) {
    std::transform(str.begin(), str.end(), str.begin(), ::toupper); // <- will not compile
    std::cout << str<< std::endl;
    return;
}

template<typename First, typename ... Strings>
void Capitalize_And_Output(First&& str, Strings&&... rest) {
    std::transform(str.begin(), str.end(), str.begin(), ::toupper); //<- will not compile
    std::cout << str << " ";
    Capitalize_And_Output(std::forward<Strings>(rest)...); // don't forget perfect forwarding.
    return;
}

此外,在main中你必须明确使用std :: string构造函数

    Capitalize_And_Output(std::string("hello"),planet,earth);  // this will work

编辑:不确定如何处理the standard中描述的此问题。