Overload between rvalue reference and const lvalue reference in template - c++

I want to overload two functions based on whether the argument is a temporary object, so I write code like this:
#include <iostream>
void f(int &&)
{
std::cout << "&&" << std::endl;
}
void f(const int&)
{
std::cout << "const &" << std::endl;
}
int main()
{
int i;
f(i);
f(i + 1);
}
And it corrently output:
const &
&&
However, when I change the code to use template like this:
#include <iostream>
template <typename T>
void f(T &&)
{
std::cout << "&&" << std::endl;
}
template <typename T>
void f(const T&)
{
std::cout << "const &" << std::endl;
}
int main()
{
int i;
f(i);
f(i + 1);
}
The output becomes:
&&
&&
What's the problem? How can I optimize for moveable temporary object when using template?
edit:
Actually, this is a test code when I read C++ Primer. It says:
template <typename T> void f(T&&); // binds to nonconst rvalues
template <typename T> void f(const T&); // lvalues and const rvalues
After my experiment, it seems the book makes a mistake here.

template <typename T>
void f(T &&)
{
std::cout << "&&" << std::endl;
}
Uses universal forwarding reference and allows any types with reference collapsing.
You have to use T with a no deducing context as wrapping your code into a struct:
template <typename T>
struct helper
{
void f(T &&)
{
std::cout << "&&" << std::endl;
}
void f(const T&)
{
std::cout << "const &" << std::endl;
}
};
template <typename T>
void f(T &&t)
{
helper<typename std::decay<T>::type>().f(std::forward<T>(t));
}
Live example

Related

How to pass reference of noncopyable type to SFINAE "catch-all" overload function?

I want to handle noncopyable type by reference when SFINAE get unkown input, my code below can't work, is there a better way?
#include <iostream>
#include <functional>
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void data_type(T const& t) {
std::cout << "integer" << std::endl;
}
void data_type(...) {
std::cout << "catch unknown" << std::endl;
}
int main() {
struct noncopyable_type {
int i;
noncopyable_type() {}
noncopyable_type(const noncopyable_type&) = delete;
};
int i;
noncopyable_type s;
// first try
data_type(i); // ok
data_type(s); // error: call to deleted constructor
// try again
data_type(std::cref(i)); // ok, but the type is std::reference_wrapper, not integer
data_type(std::cref(s)); // ok
}
There are probably many ways, this is the first one that came to mind. Live demo
#include <iostream>
#include <functional>
template <typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
void data_type(T const& t) {
std::cout << "integer" << std::endl;
}
template <typename ... T,
typename = typename std::enable_if<sizeof...(T)==1>::type>
void data_type(T const&...) {
std::cout << "unknown" << std::endl;
}
int main() {
struct noncopyable_type {
noncopyable_type() {}
noncopyable_type(const noncopyable_type&) = delete;
};
int i;
noncopyable_type s;
// first try
data_type(i); // ok
data_type(s); // ok
}
In C++17 I would just use if constexpr.
We rarely need to use the ... trick anymore. With concepts, we can get the overload resolution behaviour we need without having to play tricks with the parameter declaration clause:
template <typename T>
requires std::integral<T>
void data_type(T const& t) {
std::cout << "integer" << std::endl;
}
template <typename T>
void data_type(T const& t) {
std::cout << "unknown" << std::endl;
}
The first overload is more constrained than the second one, so the second one will only be used when the first one is not applicable due to its constraint not being satisfied.
Note that the first overload may equivalently be written like so:
template <std::integral T>
void data_type(T const& t) {
std::cout << "integer" << std::endl;
}
It's very unclear to me what the actual problem you're trying to solve is, but there's a few possibly better ways to approach this.
With if constexpr
template <typename T>
void data_type(T const&) {
if constexpr (std::is_integral_v<T>) {
std::cout << "integral\n";
} else {
std::cout << "unknown\n";
}
}
If (as I suspect) your goal is to not bind a reference to integral types (for whatever reason) you can get fancier with C++20 concepts
template <std::integral T>
void data_type(T) {
std::cout << "integral\n";
}
template <typename T> requires (!std::integral<T>)
void data_type(T const&) {
std::cout << "unknown\n";
}

Why no overload chosen on forward reference for function templates?

Snippet:
#include <iostream>
template<typename T>
struct Printer{};
template<typename T>
void test(T&&)
{
std::cout << "Primary template called\n";
}
template<typename T>
void test(Printer<T>&&)
{
std::cout << "Specialized template called\n";
}
int main()
{
auto t = Printer<int>{};
test(0);
test(t);
}
Here is the demo
Why is two times Primary template called printed?
If one removes the forward reference from the second template, then the Printer overload is chosen.
Why is it not chosen with &&?
Forwarding reference only works for T&&, not C<T>&& nor const T&&.
test(0); // Call test(T&&) with T=int
test(t); // Call test(T&&) with T=Printer<int>&

C++ operator lookup misunderstanding

I have a trouble with next case:
template<typename T>
void test(const T &ref){
cout << "By reference";
}
template<typename T>
void test(const T *ptr){
cout << "By pointer";
}
Any parameter that I sent to the test() method will always pass to overloading with reference. Even this:
int *p = 0; test(p);
Can someone tell me why reference has so high priority and the place in standart where to read about this.
Oh... I was inattentive! I have to specify both const and non-const overloading for a pointer case:
template<typename T>
void test(const T &ref){
cout << "By reference";
}
template<typename T>
void test(T *ptr){
cout << "By pointer";
}
template<typename T>
void test(const T *ptr){
cout << "By const pointer";
}
Because const T * means that T is const but not T *.
#include <iostream>
template<typename T>
void test(const T &ref){
std::cout << "By reference\n";
}
template<typename T>
void test( T * const ptr){
std::cout << "By pointer\n";
}
int main()
{
int *p;
test(p);
return 0;
}
You can also use typedef T * PtrT, and then change T * const to const PtrT.
template <typename T>
using PtrT = T *;
template<typename T>
void test(const PtrT<T> ptr){
std::cout << "By pointer\n";
}
Can you check which type is used in template, is it int or int*? I suspect that you inspecting T to be int, but compiler interpret T as a int* and use reference template.
Try to use
test<int>(p);
to specify type explicity

difference between array pointer to array reference

I want to write a function which distinguish between arrays and pointers. This is needed in order to figure size of literal strings. I tried:
template<typename Ty>
void f(const Ty* rhs) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
template<typename Ty, size_t Dm>
void f(const Ty(&rhs)[Dm]) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
int main(int, char*[]) {
const char arr0[] = "test2";
const char* ptr = "test3";
const char arr6[6] = "test4";
f("test1");
f(arr0);
f(ptr);
f(arr6);
return 0;
}
But the compiler (VS2013) tells me that the call is ambiguous. Any hints?
Thanks in advance.
Unfortunately, the call are ambiguous.
As workaround, you may add an extra layer:
template<typename Ty>
void f_pointer(const Ty* rhs) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
template<typename Ty, size_t Dm>
void f_array(const Ty(&rhs)[Dm]) {
std::cout << __FUNCTION__ << rhs << std::endl;
}
template<typename T>
std::enable_if_t<std::is_array<T>::value>
f(const T&t)
{
f_array(t);
}
template<typename T>
std::enable_if_t<!std::is_array<T>::value>
f(const T&t)
{
f_pointer(t);
}
Live Demo
An alternative to Jarod42's answer (which works) is to use class template specialization:
#include <iostream>
#include <type_traits>
template <typename T, bool is_array>
struct f_helper {
static void print_type (T& arg) {
std::cout << arg << " is an array\n";
}
};
template <typename T>
struct f_helper<T, false> {
static void print_type (T arg) {
std::cout << arg << " is not an array\n";
}
};
template <typename T>
void f (T& arg) {
f_helper<T, std::is_array<T>::value>::print_type (arg);
}
int main(int, char*[]) {
const char arr0[] = "test2";
const char* ptr = "test3";
const char arr6[6] = "test4";
f("test1");
f(arr0);
f(ptr);
f(arr6);
return 0;
}
Live demo
Change the reference to a pointer in the second overload of the function template:
template<typename T, size_t S> void f(T(&)[S]){
cout << "array with size " << S << "\n";
}
template <typename T> void f(T*&) {
cout << "pointer \n";
}
Live Demo
I don't believe it's possible to do what you're trying the way you're trying. The following statements evaluate to the same thing:
int* var
int var[]
It's all a matter of syntax sugar. Moreover, adding a size to the array on the function header has only the benefit of warning the user of the expected array size. Therefore the two are also equivalent (as far as the compiler is concerned):
void f(int* v)
void f(int v[8])

Hiding move semantics behind single function

e.g. I have a function that can handle const T & and T && values:
template <typename T>
/* ... */ foo(const T &) {
std::cout << "const T & as arg" << std::endl;
}
template <typename T>
/* ... */ foo(T &&) {
std::cout << "T && as arg" << std::endl;
}
Is there a way that I can write a single function, that handles both types automatically? As in:
template <typename T>
/* ... */ bar(T t) {
foo(t);
}
So that:
T a;
bar(a); // Handles as const T &
T b;
bar(std::move(b)); // Handles as T &&
Thank you!
You can use reference collapsing and std::forward to forward the argument to the foo function:
template <typename T>
/* ... */ bar(T&& t) {
foo(std::forward<T>(t));
}
Please notice that your foo function will accept rvalues, constant lvalues and non-const lvalues. As an example, given:
const int x = 456;
int y = 123;
then:
foo(123); // foo(T&&)
foo(x); // foo(const T&)
foo(y); // foo(T&&)
Live demo