Domanda

I would like to write a variadic template function that accepts rvalues and lvalue references. It would capitalize std::strings, and display each parameter after they are capitalized. All lvalues should remain capitalized after the function has ended (i.e. have lvalues passed by reference).
For example, I would like this behaviour:

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"

How can I get this above chunk of code to compile and work as shown?

So far, I am able to output the information, but I do not know how to handle the capitalization of the two different value types. The following code will compile, because I have commented out the lines that do not work.

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

How can I get this to work?
Maybe the transform function doesn't work, because the rvalues are actually a different type? They are char*s?

What is going through my head:
Do I have to do something with type traits?
Something with R value references?
Something with universal reverences(not really sure what that is though)?

Please correct any misuse of the terminology!

È stato utile?

Soluzione

First, the underlying reason why your program is not working is not only related to rvalue/lvalue compatibility. To show that consider this version that only pass the arguments by value

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

In this case your main code will not work either. To show that consider the following

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;
}

The line

Capitalize_And_Output("hello",planet,earth);

does not work because compiler consider the first argument "a const char" (char does not have .begin() and .end() iterators!)!. In fact, I get the following error message in g++

instantiated from here
teste.cpp:14:5: error: request for member ‘begin’ in ‘str’, which is of non-class type ‘const char*’

Now back to your original lvalue/rvalue question (collapsing rule says that && & = &. Here is the solution. You also cannot forget to "perfect forward" the rvalue references.

Then, a summary of the final answer is

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;
}

plus the fact that in main you must explicit use std::string constructor

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

EDIT: not sure how to handle this problem described in the standard.

Altri suggerimenti

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

Read about Universal references. It's a much better way (IMHO) of thinking about these things and I feel his video explains rvalue refrences in general very well.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top