Specialisation for template class constructor - c++

I am new to templates in C++.
Can anyone explain why my specialised constructor never gets executed.
It works when I remove the const and reference operator.
#include<iostream>
#include<string>
using namespace std;
template<typename T>
class CData
{
public:
CData(const T&);
CData(const char*&);
private:
T m_Data;
};
template<typename T>
CData<T>::CData(const T& Val)
{
cout << "Template" << endl;
m_Data = Val;
}
template<>
CData<char*>::CData(const char* &Str)
{
cout << "Char*" << endl;
m_Data = new char[strlen(Str) + 1];
strcpy(m_Data, Str);
}
void main()
{
CData<int> obj1(10);
CData<char*> obj2("Hello");
}
The output is
Template
Template

Because you cannot bind "Hello" to a const char*&.
The information dyp added in comments is quite interesting:
A string literal is an array lvalue, which can be converted to a pointer prvalue. A pointer prvalue cannot bind to a non-const lvalue reference like const char* &
Which means you can actually make it work by replacing const char*& by const char* const&, or even const char* && in c++11, not sure if this is really smart in your use case though.

UPDATE I got everything wrong, rewrote the answer completely.
First, this constructor
template<>
CData<char*>::CData(const char* &Str)
is not a specialization of CData(const T&), because the Str parameter here is a non-const reference to pointer to const char. So it's a definition of non-templated constructor CData(const char*&).
Second, "Hello" has type "array of n const char" (see What is the type of string literals in C and C++?) so it can't be converted to non-const reference. This is why "Template" constructor is called.
The correct specialization is
template<>
CData<char*>::CData(char* const& Str)
That is, it accepts a const reference to char*.
And after that you should remove CData(const char*&), unless you need for example this code to compile:
const char* foo = "foo";
CData<int> obj2(foo);
So here is the code:
template<typename T>
class CData
{
public:
CData(const T&);
private:
T m_Data;
};
template<typename T>
CData<T>::CData(const T& Val)
{
....
}
template<>
CData<char*>::CData(char* const& Str)
{
....
}
// warning: deprecated conversion from string constant to 'char*'
CData<char*> obj2("Hello"); // calls CData(char* const&)
The proper way of fixing above warning is to add another specialization:
template<>
CData<const char*>::CData(const char* const& Str)
{
...
}
CData<const char*> obj2("Hello"); // calls CData(const char* const&)

Related

const char pointer template specialization

I have this piece of code:
template<typename T>
void test(const T& p);
And I wanted to specialize template with const char* to handle string literals. So I added:
template<>
void test(const char* const& p) { std::cout << "test2\n"; }
But it turns out it doesn't work with call:
test("abc");
I know that "abc" is not technically const char pointer so I should write this:
template<size_t N>
void test(const char (&p)[N]) { std::cout << "test3\n"; }
But I don't want to differientiate between string literal and const char*, eg.:
const char* s = "abc";
test(s); test("abc"); // I want it to call the same function
So I found out that if you write "template specialization" (which is actually just overload) like this:
void test(const char* const& p) { std::cout << "test4\n"; }
it works and test("abc") and test(s) calls the same function. Why is that happening? And can I write actual template specialization for const char* and string literals because above feels like a hack - not the big one but still.
You can add another template, like so:
template<typename T>
void test(const T* p);
And then you can do:
template<>
void test(const char* p) { std::cout << "test2\n"; }
However, I don't see anything wrong with just adding a plain-old non-templated function overload to handle the const char * case:
void test(const char* p) { std::cout << "test4\n"; }
In fact, I much prefer it (because otherwise you get a separate instantiation for each different string you pass to test).

What is the use of const rvalue references? [duplicate]

I guess not, but I would like to confirm. Is there any use for const Foo&&, where Foo is a class type?
They are occasionally useful. The draft C++0x itself uses them in a few places, for example:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
The above two overloads ensure that the other ref(T&) and cref(const T&) functions do not bind to rvalues (which would otherwise be possible).
Update
I've just checked the official standard N3290, which unfortunately isn't publicly available, and it has in 20.8 Function objects [function.objects]/p2:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Then I checked the most recent post-C++11 draft, which is publicly available, N3485, and in 20.8 Function objects [function.objects]/p2 it still says:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
The semantics of getting a const rvalue reference (and not for =delete) is for saying:
we do not support the operation for lvalues!
even though, we still copy, because we can't move the passed resource, or because there is no actual meaning for "moving" it.
The following use case could have been IMHO a good use case for rvalue reference to const, though the language decided not to take this approach (see original SO post).
The case: smart pointers constructor from raw pointer
It would usually be advisable to use make_unique and make_shared, but both unique_ptr and shared_ptr can be constructed from a raw pointer. Both constructors get the pointer by value and copy it. Both allow (i.e. in the sense of: do not prevent) a continuance usage of the original pointer passed to them in the constructor.
The following code compiles and results with double free:
int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;
Both unique_ptr and shared_ptr could prevent the above if their relevant constructors would expect to get the raw pointer as a const rvalue, e.g. for unique_ptr:
unique_ptr(T* const&& p) : ptr{p} {}
In which case the double free code above would not compile, but the following would:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Note that ptr could still be used after it was moved, so the potential bug is not totally gone. But if user is required to call std::move such a bug would fall into the common rule of: do not use a resource that was moved.
One can ask: OK, but why T* const&& p?
The reason is simple, to allow creation of unique_ptr from const pointer. Remember that const rvalue reference is more generic than just rvalue reference as it accepts both const and non-const. So we can allow the following:
int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };
this wouldn't go if we would expect just rvalue reference (compilation error: cannot bind const rvalue to rvalue).
Anyhow, this is too late to propose such a thing. But this idea does present a reasonable usage of an rvalue reference to const.
They are allowed and even functions ranked based on const, but since you can't move from const object referred by const Foo&&, they aren't useful.
Besides std::ref, the standard library also uses const rvalue reference in std::as_const for the same purpose.
template <class T>
void as_const(const T&&) = delete;
It is also used as return value in std::optional when getting the wrapped value:
constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;
As well as in std::get:
template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;
This is presumably in order to maintain the value category as well as constness of the wrapper when accessing the wrapped value.
This makes a difference whether const rvalue ref-qualified functions can be called on the wrapped object. That said, I don't know any uses for const rvalue ref qualified functions.
I can't think of a situation where this would be useful directly, but it might be used indirectly:
template<class T>
void f(T const &x) {
cout << "lvalue";
}
template<class T>
void f(T &&x) {
cout << "rvalue";
}
template<class T>
void g(T &x) {
f(T());
}
template<class T>
void h(T const &x) {
g(x);
}
The T in g is T const, so f's x is an T const&&.
It is likely this results in a comile error in f (when it tries to move or use the object), but f could take an rvalue-ref so that it cannot be called on lvalues, without modifying the rvalue (as in the too simple example above).
Perhaps it could be considered useful in this context (coliru link):
#include <iostream>
// Just a simple class
class A {
public:
explicit A(const int a) : a_(a) {}
int a() const { return a_; }
private:
int a_;
};
// Returning a const value - shouldn't really do this
const A makeA(const int a) {
return A{a};
}
// A wrapper class referencing A
class B {
public:
explicit B(const A& a) : a_(a) {}
explicit B(A&& a) = delete;
// Deleting the const&& prevents this mistake from compiling
//explicit B(const A&& a) = delete;
int a() const { return a_.a(); }
private:
const A& a_;
};
int main()
{
// This is a mistake since makeA returns a temporary that B
// attempts to reference.
auto b = B{makeA(3)};
std::cout << b.a();
}
It prevents the mistake being compiled. There are clearly a bunch of other problems with this code that compiler warnings do pick up, but perhaps the const&& helps?
Rvalue references are meant to allow moving data.
So in the vast majority of case its use is pointless.
The main edge case you will find it is to prevent people to call a function with an rvalue:
template<class T>
void fun(const T&& a) = delete;
The const version will cover all the edge cases, contrary to the non const version.
Here is why, consider this example:
struct My_object {
int a;
};
template<class T>
void fun(const T& param) {
std::cout << "const My_object& param == " << param.a << std::endl;
}
template<class T>
void fun( T& param) {
std::cout << "My_object& param == " << param.a << std::endl;
}
int main() {
My_object obj = {42};
fun( obj );
// output: My_object& param == 42
const My_object const_obj = {64};
fun( const_obj );
// output: const My_object& param == 64
fun( My_object{66} );
// const My_object& param == 66
return 0;
}
Now if you'd like to prevent someone using fun( My_object{66} ); since in the present case, it will be converted to const My_object&, you need to define:
template<class T>
void fun(T&& a) = delete;
And now fun( My_object{66} ); will throw an error, however, if some smarty pants programmer decides to write:
fun<const My_object&>( My_object{1024} );
// const My_object& param == 1024
This will work again and call the const lvalue overload version of that function... Fortunately we can put an end to such profanity adding const to our deleted overload:
template<class T>
void fun(const T&& a) = delete;
It's somewhat disturbing how pretty much everyone in this thread (with the exception of #FredNurk and #lorro) misunderstand how const works, so allow me to chime in.
Const reference only forbids modifying the immediate contents of the class. Not only do we have static and mutable members which we very well can modify through a const reference; but we also can modify the contents of the class stored in a memory location referenced by a non-static, non-mutable pointer - as long as we don't modify the pointer itself.
Which is exactly the case of an extremely common Pimpl idiom. Consider:
// MyClass.h
class MyClass
{
public:
MyClass();
MyClass(int g_meat);
MyClass(const MyClass &&other); // const rvalue reference!
~MyClass();
int GetMeat() const;
private:
class Pimpl;
Pimpl *impl {};
};
// MyClass.cpp
class MyClass::Pimpl
{
public:
int meat {42};
};
MyClass::MyClass() : impl {new Pimpl} { }
MyClass::MyClass(int g_meat) : MyClass()
{
impl->meat = g_meat;
}
MyClass::MyClass(const MyClass &&other) : MyClass()
{
impl->meat = other.impl->meat;
other.impl->meat = 0;
}
MyClass::~MyClass()
{
delete impl;
}
int MyClass::GetMeat() const
{
return impl->meat;
}
// main.cpp
const MyClass a {100500};
MyClass b (std::move(a)); // moving from const!
std::cout << a.GetMeat() << "\n"; // returns 0, b/c a is moved-from
std::cout << b.GetMeat() << "\n"; // returns 100500
Behold - a fully functional, const-correct move constructor which accepts const rvalue references.

C++ passing a const pointer by const reference

Hmm a strange one in VC2012 I can't seem to work out the syntax for passing a const pointer by const reference into a function of a templated class whose template argument is a non const pointer ie:
template<typename T>
struct Foo
{
void Add( const T& Bar ) { printf(Bar); }
};
void main()
{
Foo<char*> foo;
const char* name = "FooBar";
foo.Add(name); // Causes error
}
So I've simplified my problem here but basically I want the argument to 'Add' to have a const T ie const char*. I've tried:
void Add( const (const T)& Bar );
typedef const T ConstT;
void Add( const (ConstT)& Bar );
void Add( const typename std::add_const<T>::type& Bar );
None of which work. The exact error I'm getting is:
error C2664: 'Foo<T>::Add' : cannot convert parameter 1 from 'const char *' to 'char *const &'
with
[
T=char *
]
Conversion loses qualifiers
which I can see is correct but how do I solve it without const casting 'name' to be non const.
There is a strong difference between a pointer to a constant object (T const*, or const T*) and a constant pointer to a non-constant object (T * const). In your case the signature of the member Add is:
void Foo<char *>::Add(char * const& ); // reference to a constant pointer to a
// non-constant char
I usually recommend that people drop the use of const on the left hand side exactly for this reason, as beginners usually confuse typedefs (or deduced types) with type substitution and when they read:
const T& [T == char*]
They misinterpret
const char*&
If the const is placed in the right place:
T const &
Things are simpler for beginners, as plain mental substitution works:
char * const &
A different problem than what you are asking, but maybe what you think you want, is:
Given a type T have a function that takes a U that is const T if T is not a pointer type, or X const * if T is a pointer to X
template <typename T>
struct add_const_here_or_there {
typedef T const type;
};
template <typename T>
struct add_const_here_or_there<T*> {
typedef T const * type;
};
Then you can use this in your signature:
template <typename T>
void Foo<T>::Add( const typename add_const_here_or_there<T>::type & arg ) {
...
Note that I am adding two const in the signature, so in your case char* will map to char const * const &, as it seems that you want to pass a const& to something and you also want the pointed type to be const.
You might have wondered as of the name for the metafunction: *add_const_here_or_there*, it is like that for a reason: there is no simple way of describing what you are trying to do, which is usually a code smell. But here you have your solution.
It looks like your issue here as that as soon as you have a pointer type mapped to a template type, you can no longer add const-ness to the pointed-to type, only to the pointer itself. What it looks like you're trying to do is automatically add constness to the parameter of your function (so if T is char* the function should accept const char* const& rather than char* const& as you've written). The only way to do that is with another template to add constness to the pointee for pointer types, as follows. I took the liberty of including missing headers and correcting the signature of main:
#include <cstdio>
template<typename T>
struct add_const_to_pointee
{
typedef T type;
};
template <typename T>
struct add_const_to_pointee<T*>
{
typedef const T* type;
};
template<typename T>
struct Foo
{
void Add( typename add_const_to_pointee<T>::type const & Bar ) { printf(Bar); }
};
int main()
{
Foo<char*> foo;
const char* name = "FooBar";
foo.Add(name); // Causes error
}
As mentioned in another another however, this issue just goes away if you use std::string instead of C-style strings.
You need to change the template argument to your Foo object to Foo<const char*>. Because if T=char*, then const T=char*const, not const char*. Trying to coerce it to work is not a good idea and would probably result in undefined behavior.
Use:
Foo<const char*> foo;
const char* name = "FooBar";
foo.Add(name);
And write int main() instead of void main()
If passing const char* instead of char* to Foo is not an option you can finesse the correct type with std::remove_pointer. This will remove the pointer modifier and allow you to provide a more explicit type.
#include <type_traits>
template<typename T>
struct Foo
{
void Add(typename std::remove_pointer<T>::type const*& Bar ) { printf(Bar); }
};
To prevent the pointer value from being modified you can declare the reference as const as well.
void Add(typename std::remove_pointer<T>::type const* const& Bar )
{ Bar = "name"; } // <- fails
If you need to reduce the type from say a pointer to pointer you can use std::decay along with std::remove_pointer
void Add(typename std::remove_pointer<typename std::decay<T>::type>::type const*& Bar)
{
printf(Bar);
}
This really depends on what your requirements for T are. I suggest assuming only the base type (e.g. char) is passed as T and building reference and pointer types from that.

Why isn't const T && (rvalue reference) an error [duplicate]

I guess not, but I would like to confirm. Is there any use for const Foo&&, where Foo is a class type?
They are occasionally useful. The draft C++0x itself uses them in a few places, for example:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
The above two overloads ensure that the other ref(T&) and cref(const T&) functions do not bind to rvalues (which would otherwise be possible).
Update
I've just checked the official standard N3290, which unfortunately isn't publicly available, and it has in 20.8 Function objects [function.objects]/p2:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Then I checked the most recent post-C++11 draft, which is publicly available, N3485, and in 20.8 Function objects [function.objects]/p2 it still says:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
The semantics of getting a const rvalue reference (and not for =delete) is for saying:
we do not support the operation for lvalues!
even though, we still copy, because we can't move the passed resource, or because there is no actual meaning for "moving" it.
The following use case could have been IMHO a good use case for rvalue reference to const, though the language decided not to take this approach (see original SO post).
The case: smart pointers constructor from raw pointer
It would usually be advisable to use make_unique and make_shared, but both unique_ptr and shared_ptr can be constructed from a raw pointer. Both constructors get the pointer by value and copy it. Both allow (i.e. in the sense of: do not prevent) a continuance usage of the original pointer passed to them in the constructor.
The following code compiles and results with double free:
int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;
Both unique_ptr and shared_ptr could prevent the above if their relevant constructors would expect to get the raw pointer as a const rvalue, e.g. for unique_ptr:
unique_ptr(T* const&& p) : ptr{p} {}
In which case the double free code above would not compile, but the following would:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Note that ptr could still be used after it was moved, so the potential bug is not totally gone. But if user is required to call std::move such a bug would fall into the common rule of: do not use a resource that was moved.
One can ask: OK, but why T* const&& p?
The reason is simple, to allow creation of unique_ptr from const pointer. Remember that const rvalue reference is more generic than just rvalue reference as it accepts both const and non-const. So we can allow the following:
int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };
this wouldn't go if we would expect just rvalue reference (compilation error: cannot bind const rvalue to rvalue).
Anyhow, this is too late to propose such a thing. But this idea does present a reasonable usage of an rvalue reference to const.
They are allowed and even functions ranked based on const, but since you can't move from const object referred by const Foo&&, they aren't useful.
Besides std::ref, the standard library also uses const rvalue reference in std::as_const for the same purpose.
template <class T>
void as_const(const T&&) = delete;
It is also used as return value in std::optional when getting the wrapped value:
constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;
As well as in std::get:
template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;
This is presumably in order to maintain the value category as well as constness of the wrapper when accessing the wrapped value.
This makes a difference whether const rvalue ref-qualified functions can be called on the wrapped object. That said, I don't know any uses for const rvalue ref qualified functions.
I can't think of a situation where this would be useful directly, but it might be used indirectly:
template<class T>
void f(T const &x) {
cout << "lvalue";
}
template<class T>
void f(T &&x) {
cout << "rvalue";
}
template<class T>
void g(T &x) {
f(T());
}
template<class T>
void h(T const &x) {
g(x);
}
The T in g is T const, so f's x is an T const&&.
It is likely this results in a comile error in f (when it tries to move or use the object), but f could take an rvalue-ref so that it cannot be called on lvalues, without modifying the rvalue (as in the too simple example above).
Perhaps it could be considered useful in this context (coliru link):
#include <iostream>
// Just a simple class
class A {
public:
explicit A(const int a) : a_(a) {}
int a() const { return a_; }
private:
int a_;
};
// Returning a const value - shouldn't really do this
const A makeA(const int a) {
return A{a};
}
// A wrapper class referencing A
class B {
public:
explicit B(const A& a) : a_(a) {}
explicit B(A&& a) = delete;
// Deleting the const&& prevents this mistake from compiling
//explicit B(const A&& a) = delete;
int a() const { return a_.a(); }
private:
const A& a_;
};
int main()
{
// This is a mistake since makeA returns a temporary that B
// attempts to reference.
auto b = B{makeA(3)};
std::cout << b.a();
}
It prevents the mistake being compiled. There are clearly a bunch of other problems with this code that compiler warnings do pick up, but perhaps the const&& helps?
Rvalue references are meant to allow moving data.
So in the vast majority of case its use is pointless.
The main edge case you will find it is to prevent people to call a function with an rvalue:
template<class T>
void fun(const T&& a) = delete;
The const version will cover all the edge cases, contrary to the non const version.
Here is why, consider this example:
struct My_object {
int a;
};
template<class T>
void fun(const T& param) {
std::cout << "const My_object& param == " << param.a << std::endl;
}
template<class T>
void fun( T& param) {
std::cout << "My_object& param == " << param.a << std::endl;
}
int main() {
My_object obj = {42};
fun( obj );
// output: My_object& param == 42
const My_object const_obj = {64};
fun( const_obj );
// output: const My_object& param == 64
fun( My_object{66} );
// const My_object& param == 66
return 0;
}
Now if you'd like to prevent someone using fun( My_object{66} ); since in the present case, it will be converted to const My_object&, you need to define:
template<class T>
void fun(T&& a) = delete;
And now fun( My_object{66} ); will throw an error, however, if some smarty pants programmer decides to write:
fun<const My_object&>( My_object{1024} );
// const My_object& param == 1024
This will work again and call the const lvalue overload version of that function... Fortunately we can put an end to such profanity adding const to our deleted overload:
template<class T>
void fun(const T&& a) = delete;
It's somewhat disturbing how pretty much everyone in this thread (with the exception of #FredNurk and #lorro) misunderstand how const works, so allow me to chime in.
Const reference only forbids modifying the immediate contents of the class. Not only do we have static and mutable members which we very well can modify through a const reference; but we also can modify the contents of the class stored in a memory location referenced by a non-static, non-mutable pointer - as long as we don't modify the pointer itself.
Which is exactly the case of an extremely common Pimpl idiom. Consider:
// MyClass.h
class MyClass
{
public:
MyClass();
MyClass(int g_meat);
MyClass(const MyClass &&other); // const rvalue reference!
~MyClass();
int GetMeat() const;
private:
class Pimpl;
Pimpl *impl {};
};
// MyClass.cpp
class MyClass::Pimpl
{
public:
int meat {42};
};
MyClass::MyClass() : impl {new Pimpl} { }
MyClass::MyClass(int g_meat) : MyClass()
{
impl->meat = g_meat;
}
MyClass::MyClass(const MyClass &&other) : MyClass()
{
impl->meat = other.impl->meat;
other.impl->meat = 0;
}
MyClass::~MyClass()
{
delete impl;
}
int MyClass::GetMeat() const
{
return impl->meat;
}
// main.cpp
const MyClass a {100500};
MyClass b (std::move(a)); // moving from const!
std::cout << a.GetMeat() << "\n"; // returns 0, b/c a is moved-from
std::cout << b.GetMeat() << "\n"; // returns 100500
Behold - a fully functional, const-correct move constructor which accepts const rvalue references.

Do rvalue references to const have any use?

I guess not, but I would like to confirm. Is there any use for const Foo&&, where Foo is a class type?
They are occasionally useful. The draft C++0x itself uses them in a few places, for example:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
The above two overloads ensure that the other ref(T&) and cref(const T&) functions do not bind to rvalues (which would otherwise be possible).
Update
I've just checked the official standard N3290, which unfortunately isn't publicly available, and it has in 20.8 Function objects [function.objects]/p2:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
Then I checked the most recent post-C++11 draft, which is publicly available, N3485, and in 20.8 Function objects [function.objects]/p2 it still says:
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;
The semantics of getting a const rvalue reference (and not for =delete) is for saying:
we do not support the operation for lvalues!
even though, we still copy, because we can't move the passed resource, or because there is no actual meaning for "moving" it.
The following use case could have been IMHO a good use case for rvalue reference to const, though the language decided not to take this approach (see original SO post).
The case: smart pointers constructor from raw pointer
It would usually be advisable to use make_unique and make_shared, but both unique_ptr and shared_ptr can be constructed from a raw pointer. Both constructors get the pointer by value and copy it. Both allow (i.e. in the sense of: do not prevent) a continuance usage of the original pointer passed to them in the constructor.
The following code compiles and results with double free:
int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;
Both unique_ptr and shared_ptr could prevent the above if their relevant constructors would expect to get the raw pointer as a const rvalue, e.g. for unique_ptr:
unique_ptr(T* const&& p) : ptr{p} {}
In which case the double free code above would not compile, but the following would:
std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) }; // ok, rvalue
Note that ptr could still be used after it was moved, so the potential bug is not totally gone. But if user is required to call std::move such a bug would fall into the common rule of: do not use a resource that was moved.
One can ask: OK, but why T* const&& p?
The reason is simple, to allow creation of unique_ptr from const pointer. Remember that const rvalue reference is more generic than just rvalue reference as it accepts both const and non-const. So we can allow the following:
int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };
this wouldn't go if we would expect just rvalue reference (compilation error: cannot bind const rvalue to rvalue).
Anyhow, this is too late to propose such a thing. But this idea does present a reasonable usage of an rvalue reference to const.
They are allowed and even functions ranked based on const, but since you can't move from const object referred by const Foo&&, they aren't useful.
Besides std::ref, the standard library also uses const rvalue reference in std::as_const for the same purpose.
template <class T>
void as_const(const T&&) = delete;
It is also used as return value in std::optional when getting the wrapped value:
constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;
As well as in std::get:
template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;
This is presumably in order to maintain the value category as well as constness of the wrapper when accessing the wrapped value.
This makes a difference whether const rvalue ref-qualified functions can be called on the wrapped object. That said, I don't know any uses for const rvalue ref qualified functions.
I can't think of a situation where this would be useful directly, but it might be used indirectly:
template<class T>
void f(T const &x) {
cout << "lvalue";
}
template<class T>
void f(T &&x) {
cout << "rvalue";
}
template<class T>
void g(T &x) {
f(T());
}
template<class T>
void h(T const &x) {
g(x);
}
The T in g is T const, so f's x is an T const&&.
It is likely this results in a comile error in f (when it tries to move or use the object), but f could take an rvalue-ref so that it cannot be called on lvalues, without modifying the rvalue (as in the too simple example above).
Perhaps it could be considered useful in this context (coliru link):
#include <iostream>
// Just a simple class
class A {
public:
explicit A(const int a) : a_(a) {}
int a() const { return a_; }
private:
int a_;
};
// Returning a const value - shouldn't really do this
const A makeA(const int a) {
return A{a};
}
// A wrapper class referencing A
class B {
public:
explicit B(const A& a) : a_(a) {}
explicit B(A&& a) = delete;
// Deleting the const&& prevents this mistake from compiling
//explicit B(const A&& a) = delete;
int a() const { return a_.a(); }
private:
const A& a_;
};
int main()
{
// This is a mistake since makeA returns a temporary that B
// attempts to reference.
auto b = B{makeA(3)};
std::cout << b.a();
}
It prevents the mistake being compiled. There are clearly a bunch of other problems with this code that compiler warnings do pick up, but perhaps the const&& helps?
Rvalue references are meant to allow moving data.
So in the vast majority of case its use is pointless.
The main edge case you will find it is to prevent people to call a function with an rvalue:
template<class T>
void fun(const T&& a) = delete;
The const version will cover all the edge cases, contrary to the non const version.
Here is why, consider this example:
struct My_object {
int a;
};
template<class T>
void fun(const T& param) {
std::cout << "const My_object& param == " << param.a << std::endl;
}
template<class T>
void fun( T& param) {
std::cout << "My_object& param == " << param.a << std::endl;
}
int main() {
My_object obj = {42};
fun( obj );
// output: My_object& param == 42
const My_object const_obj = {64};
fun( const_obj );
// output: const My_object& param == 64
fun( My_object{66} );
// const My_object& param == 66
return 0;
}
Now if you'd like to prevent someone using fun( My_object{66} ); since in the present case, it will be converted to const My_object&, you need to define:
template<class T>
void fun(T&& a) = delete;
And now fun( My_object{66} ); will throw an error, however, if some smarty pants programmer decides to write:
fun<const My_object&>( My_object{1024} );
// const My_object& param == 1024
This will work again and call the const lvalue overload version of that function... Fortunately we can put an end to such profanity adding const to our deleted overload:
template<class T>
void fun(const T&& a) = delete;
It's somewhat disturbing how pretty much everyone in this thread (with the exception of #FredNurk and #lorro) misunderstand how const works, so allow me to chime in.
Const reference only forbids modifying the immediate contents of the class. Not only do we have static and mutable members which we very well can modify through a const reference; but we also can modify the contents of the class stored in a memory location referenced by a non-static, non-mutable pointer - as long as we don't modify the pointer itself.
Which is exactly the case of an extremely common Pimpl idiom. Consider:
// MyClass.h
class MyClass
{
public:
MyClass();
MyClass(int g_meat);
MyClass(const MyClass &&other); // const rvalue reference!
~MyClass();
int GetMeat() const;
private:
class Pimpl;
Pimpl *impl {};
};
// MyClass.cpp
class MyClass::Pimpl
{
public:
int meat {42};
};
MyClass::MyClass() : impl {new Pimpl} { }
MyClass::MyClass(int g_meat) : MyClass()
{
impl->meat = g_meat;
}
MyClass::MyClass(const MyClass &&other) : MyClass()
{
impl->meat = other.impl->meat;
other.impl->meat = 0;
}
MyClass::~MyClass()
{
delete impl;
}
int MyClass::GetMeat() const
{
return impl->meat;
}
// main.cpp
const MyClass a {100500};
MyClass b (std::move(a)); // moving from const!
std::cout << a.GetMeat() << "\n"; // returns 0, b/c a is moved-from
std::cout << b.GetMeat() << "\n"; // returns 100500
Behold - a fully functional, const-correct move constructor which accepts const rvalue references.