Skip to main content
Code Review

Return to Answer

Commonmark migration
Source Link

The biggest thing that sticks out at me is that you're passing all of your Args... parameters by value, thus pretty much making std::forward useless here. To make use of std::forward, the reference type needs to be deduced from the calling context. By itself, std::forward really doesn't do anything except a static_cast to the deduced reference type.

Basically, everywhere you have Args should be passed by Args&&:

template<typename T, typename ...Args>
void writeln(T firstArg, Args&&... extraArgs)
template<typename T, typename ...Args>
std::string format(T firstArg, Args&&... extraArgs)

As a cut down example of the difference this makes, try this example:

#include <memory>
#include <iostream>
struct s
{
 s()
 { }
 
 s(s&& )
 {
 std::cout << "Move\n";
 }
 
 s(const s& )
 {
 std::cout << "Copy\n";
 }
};
template <typename T>
void do_forward(T&& v)
{
 sink(std::forward<T>(v));
}
template <typename U>
void sink(U&& u)
{
 std::cout << "In sink\n";
}
int main()
{
 s something;
 do_forward(something);
}

If I run this, the output is:

In sink

If I change the signature of do_foward and sink to:

template <typename T>
void do_forward(T v)
template <typename U>
void sink(U u)

The output is:

Copy

Move

In sink

If we change main to just pass an actual rvalue reference instead of an lvalue:

int main()
{
 do_forward(s{});
}

We remove the first copy operation (from do_forward) but still have a move operation (in sink).

The biggest thing that sticks out at me is that you're passing all of your Args... parameters by value, thus pretty much making std::forward useless here. To make use of std::forward, the reference type needs to be deduced from the calling context. By itself, std::forward really doesn't do anything except a static_cast to the deduced reference type.

Basically, everywhere you have Args should be passed by Args&&:

template<typename T, typename ...Args>
void writeln(T firstArg, Args&&... extraArgs)
template<typename T, typename ...Args>
std::string format(T firstArg, Args&&... extraArgs)

As a cut down example of the difference this makes, try this example:

#include <memory>
#include <iostream>
struct s
{
 s()
 { }
 
 s(s&& )
 {
 std::cout << "Move\n";
 }
 
 s(const s& )
 {
 std::cout << "Copy\n";
 }
};
template <typename T>
void do_forward(T&& v)
{
 sink(std::forward<T>(v));
}
template <typename U>
void sink(U&& u)
{
 std::cout << "In sink\n";
}
int main()
{
 s something;
 do_forward(something);
}

If I run this, the output is:

In sink

If I change the signature of do_foward and sink to:

template <typename T>
void do_forward(T v)
template <typename U>
void sink(U u)

The output is:

Copy

Move

In sink

If we change main to just pass an actual rvalue reference instead of an lvalue:

int main()
{
 do_forward(s{});
}

We remove the first copy operation (from do_forward) but still have a move operation (in sink).

The biggest thing that sticks out at me is that you're passing all of your Args... parameters by value, thus pretty much making std::forward useless here. To make use of std::forward, the reference type needs to be deduced from the calling context. By itself, std::forward really doesn't do anything except a static_cast to the deduced reference type.

Basically, everywhere you have Args should be passed by Args&&:

template<typename T, typename ...Args>
void writeln(T firstArg, Args&&... extraArgs)
template<typename T, typename ...Args>
std::string format(T firstArg, Args&&... extraArgs)

As a cut down example of the difference this makes, try this example:

#include <memory>
#include <iostream>
struct s
{
 s()
 { }
 
 s(s&& )
 {
 std::cout << "Move\n";
 }
 
 s(const s& )
 {
 std::cout << "Copy\n";
 }
};
template <typename T>
void do_forward(T&& v)
{
 sink(std::forward<T>(v));
}
template <typename U>
void sink(U&& u)
{
 std::cout << "In sink\n";
}
int main()
{
 s something;
 do_forward(something);
}

If I run this, the output is:

In sink

If I change the signature of do_foward and sink to:

template <typename T>
void do_forward(T v)
template <typename U>
void sink(U u)

The output is:

Copy

Move

In sink

If we change main to just pass an actual rvalue reference instead of an lvalue:

int main()
{
 do_forward(s{});
}

We remove the first copy operation (from do_forward) but still have a move operation (in sink).

Source Link
Yuushi
  • 11.1k
  • 2
  • 31
  • 66

The biggest thing that sticks out at me is that you're passing all of your Args... parameters by value, thus pretty much making std::forward useless here. To make use of std::forward, the reference type needs to be deduced from the calling context. By itself, std::forward really doesn't do anything except a static_cast to the deduced reference type.

Basically, everywhere you have Args should be passed by Args&&:

template<typename T, typename ...Args>
void writeln(T firstArg, Args&&... extraArgs)
template<typename T, typename ...Args>
std::string format(T firstArg, Args&&... extraArgs)

As a cut down example of the difference this makes, try this example:

#include <memory>
#include <iostream>
struct s
{
 s()
 { }
 
 s(s&& )
 {
 std::cout << "Move\n";
 }
 
 s(const s& )
 {
 std::cout << "Copy\n";
 }
};
template <typename T>
void do_forward(T&& v)
{
 sink(std::forward<T>(v));
}
template <typename U>
void sink(U&& u)
{
 std::cout << "In sink\n";
}
int main()
{
 s something;
 do_forward(something);
}

If I run this, the output is:

In sink

If I change the signature of do_foward and sink to:

template <typename T>
void do_forward(T v)
template <typename U>
void sink(U u)

The output is:

Copy

Move

In sink

If we change main to just pass an actual rvalue reference instead of an lvalue:

int main()
{
 do_forward(s{});
}

We remove the first copy operation (from do_forward) but still have a move operation (in sink).

lang-cpp

AltStyle によって変換されたページ (->オリジナル) /