Question

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!

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top