【问题标题】:Variadic templates that accept both rvalue and lvalue references?接受右值和左值引用的可变参数模板?
【发布时间】:2013-10-28 00:30:12
【问题描述】:

我想编写一个接受右值和左值引用的可变参数模板函数。它将大写 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;
}

我怎样才能让它工作?
也许转换函数不起作用,因为右值实际上是不同的类型?它们是 char*s 吗?

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

请更正任何滥用术语的地方!

【问题讨论】:

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


    【解决方案1】:

    首先,您的程序无法运行的根本原因不仅与右值/左值兼容性有关。为了表明考虑这个版本只通过值传递参数

    #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);
    

    不起作用,因为编译器考虑第一个参数“a 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*’
    

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

    那么,总结一下最后的答案就是

    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 中描述的这个问题。

    【讨论】:

    • 我理解 char* / std::string 部分,但我不相信这行得通。我现在在 (&,&) 和 (&&,&&) 函数之间遇到了模棱两可的错误。
    • 对不起..您不需要重载..由于规则崩溃,它可以在不重载的情况下工作。现在它可以工作了(我测试过)。问题是我不确定如何在没有明确重载所有可能选项的情况下处理标准中的问题。
    • FWIW 在这种情况下您可以避免使用两个重载:ideone.com/cPGw07
    【解决方案2】:

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

    阅读有关通用参考的信息。这是思考这些事情的一种更好的方式(恕我直言),我觉得他的视频很好地解释了右值引用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-12
      • 2017-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多