INTRO
Ref qualifiers : A way to dissambiguate the rl-valuness of the implied object. As a quick example, take the following class
class example
{
int member;
public:
// ...
int& value() &;
// ^
int&& value() &&;
// ^^
int const& value() const&;
// ^
};
The use of this C++11 feature (syntax marked with ^), allows us to control the version of value() that will be called with
l-values
temporaries
const l-values
Practically the ref qualification applies to the classe's *this
Defaulted / Deleted functions : Specify a special member function as having the compiler generated (default) definition or inaccessible (delete). As an example take
struct type {
type(const type&) = delete;
type& operator=(const type&) = delete;
};
The above struct, achieves being non copyable with extremely clear semantics
QUESTIONs
Is it possible / valid to combine these features ?
Which are the cases where it's explicitly forbidden or bad style ?
Is there any use case / pattern for such a combination ? (Eg creating conditional interfaces based rl-valueness quick and easy)
Yes, but there's not much use, as constructors and destructors can't be ref-qualified.
You can ref-qualify assignment operators:
struct S {
S &operator=(const S &) && = default;
};
int main() {
S s;
S() = s; // OK
s = S(); // error - LHS must be an rvalue
}
However, I'm somewhat at a loss to imagine what this would be useful for.
Related
class my_class
{
...
my_class(my_class const &) = delete;
...
};
What does = delete mean in that context?
Are there any other "modifiers" (other than = 0 and = delete)?
Deleting a function is a C++11 feature:
The common idiom of "prohibiting copying" can now be expressed
directly:
class X {
// ...
X& operator=(const X&) = delete; // Disallow copying
X(const X&) = delete;
};
[...]
The "delete" mechanism can be used for any function. For example, we
can eliminate an undesired conversion like this:
struct Z {
// ...
Z(long long); // can initialize with a long long
Z(long) = delete; // but not anything less
};
= 0 means that a function is pure virtual and you cannot instantiate an object from this class. You need to derive from it and implement this method
= delete means that the compiler will not generate those constructors for you. AFAIK this is only allowed on copy constructor and assignment operator. But I am not too good at the upcoming standard.
This excerpt from The C++ Programming Language [4th Edition] - Bjarne Stroustrup book talks about the real purpose behind using =delete:
3.3.4 Suppressing Operations
Using the default copy or move for a class in a hierarchy is typically
a disaster: given only a pointer to a base, we simply don’t know what
members the derived class has, so we can’t know how to copy
them. So, the best thing to do is usually to delete the default copy
and move operations, that is, to eliminate the default definitions of
those two operations:
class Shape {
public:
Shape(const Shape&) =delete; // no copy operations
Shape& operator=(const Shape&) =delete;
Shape(Shape&&) =delete; // no move operations
Shape& operator=(Shape&&) =delete;
˜Shape();
// ...
};
Now an attempt to copy a Shape will be caught by the compiler.
The =delete mechanism is general, that is, it can be used to suppress any operation
Are there any other "modifiers" (other than = 0 and = delete)?
Since it appears no one else answered this question, I should mention that there is also =default.
https://learn.microsoft.com/en-us/cpp/cpp/explicitly-defaulted-and-deleted-functions#explicitly-defaulted-functions
The coding standards I've worked with have had the following for most of class declarations.
// coding standard: disallow when not used
T(void) = delete; // default ctor (1)
~T(void) = delete; // default dtor (2)
T(const T&) = delete; // copy ctor (3)
T(const T&&) = delete; // move ctor (4)
T& operator= (const T&) = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)
If you use any of these 6, you simply comment out the corresponding line.
Example: class FizzBus require only dtor, and thus do not use the other 5.
// coding standard: disallow when not used
FizzBuzz(void) = delete; // default ctor (1)
// ~FizzBuzz(void); // dtor (2)
FizzBuzz(const FizzBuzz&) = delete; // copy ctor (3)
FizzBuzz& operator= (const FizzBuzz&) = delete; // copy assig (4)
FizzBuzz(const FizzBuzz&&) = delete; // move ctor (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign (6)
We comment out only 1 here, and install the implementation of it else where (probably where the coding standard suggests). The other 5 (of 6) are disallowed with delete.
You can also use '= delete' to disallow implicit promotions of different sized values ... example
// disallow implicit promotions
template <class T> operator T(void) = delete;
template <class T> Vuint64& operator= (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;
A deleted function is implicitly inline
(Addendum to existing answers)
... And a deleted function shall be the first declaration of the function (except for deleting explicit specializations of function templates - deletion should be at the first declaration of the specialization), meaning you cannot declare a function and later delete it, say, at its definition local to a translation unit.
Citing [dcl.fct.def.delete]/4:
A deleted function is implicitly inline. ( Note: The one-definition
rule
([basic.def.odr])
applies to deleted definitions. — end note ] A deleted definition
of a function shall be the first declaration of the function or, for
an explicit specialization of a function template, the first
declaration of that specialization. [ Example:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
— end example )
A primary function template with a deleted definition can be specialized
Albeit a general rule of thumb is to avoid specializing function templates as specializations do not participate in the first step of overload resolution, there are arguable some contexts where it can be useful. E.g. when using a non-overloaded primary function template with no definition to match all types which one would not like implicitly converted to an otherwise matching-by-conversion overload; i.e., to implicitly remove a number of implicit-conversion matches by only implementing exact type matches in the explicit specialization of the non-defined, non-overloaded primary function template.
Before the deleted function concept of C++11, one could do this by simply omitting the definition of the primary function template, but this gave obscure undefined reference errors that arguably gave no semantic intent whatsoever from the author of primary function template (intentionally omitted?). If we instead explicitly delete the primary function template, the error messages in case no suitable explicit specialization is found becomes much nicer, and also shows that the omission/deletion of the primary function template's definition was intentional.
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t);
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
//use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}
However, instead of simply omitting a definition for the primary function template above, yielding an obscure undefined reference error when no explicit specialization matches, the primary template definition can be deleted:
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t) = delete;
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
use_only_explicit_specializations(str);
/* error: call to deleted function 'use_only_explicit_specializations'
note: candidate function [with T = std::__1::basic_string<char>] has
been explicitly deleted
void use_only_explicit_specializations(T t) = delete; */
}
Yielding a more more readable error message, where the deletion intent is also clearly visible (where an undefined reference error could lead to the developer thinking this an unthoughtful mistake).
Returning to why would we ever want to use this technique? Again, explicit specializations could be useful to implicitly remove implicit conversions.
#include <cstdint>
#include <iostream>
void warning_at_best(int8_t num) {
std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}
template< typename T >
void only_for_signed(T t) = delete;
template<>
void only_for_signed<int8_t>(int8_t t) {
std::cout << "UB safe! 1 byte, " << +t << "\n";
}
template<>
void only_for_signed<int16_t>(int16_t t) {
std::cout << "UB safe! 2 bytes, " << +t << "\n";
}
int main()
{
const int8_t a = 42;
const uint8_t b = 255U;
const int16_t c = 255;
const float d = 200.F;
warning_at_best(a); // 42
warning_at_best(b); // implementation-defined behaviour, no diagnostic required
warning_at_best(c); // narrowing, -Wconstant-conversion warning
warning_at_best(d); // undefined behaviour!
only_for_signed(a);
only_for_signed(c);
//only_for_signed(b);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = unsigned char]
has been explicitly deleted
void only_for_signed(T t) = delete; */
//only_for_signed(d);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = float]
has been explicitly deleted
void only_for_signed(T t) = delete; */
}
= delete is a feature introduce in C++11. As per =delete it will not allowed to call that function.
In detail.
Suppose in a class.
Class ABC{
Int d;
Public:
ABC& operator= (const ABC& obj) =delete
{
}
};
While calling this function for obj assignment it will not allowed. Means assignment operator is going to restrict to copy from one object to another.
New C++0x standard. Please see section 8.4.3 in the N3242 working draft
This is new thing in C++ 0x standards where you can delete an inherited function.
A small example to summarize some common usages:
class MyClass
{
public:
// Delete copy constructor:
// delete the copy constructor so you cannot copy-construct an object
// of this class from a different object of this class
MyClass(const MyClass&) = delete;
// Delete assignment operator:
// delete the `=` operator (`operator=()` class method) to disable copying
// an object of this class
MyClass& operator=(const MyClass&) = delete;
// Delete constructor with certain types you'd like to
// disallow:
// (Arbitrary example) don't allow constructing from an `int` type. Expect
// `uint64_t` instead.
MyClass(uint64_t);
MyClass(int) = delete;
// "Pure virtual" function:
// `= 0` makes this is a "pure virtual" method which *must* be overridden
// by a child class
uint32_t getVal() = 0;
}
TODO:
I still need to make a more thorough example, and run this to show some usages and output, and their corresponding error messages.
See also
https://www.stroustrup.com/C++11FAQ.html#default - section "control of defaults: default and delete"
I recently started learning about type erasures. It turned out that this technique can greatly simplify my life. Thus I tried to implement this pattern. However, I experience some problems with the copy- and move-constructor of the type erasure class.
Now, lets first have a look on the code, which is quite straight forward
#include<iostream>
class A //first class
{
private:
double _value;
public:
//default constructor
A():_value(0) {}
//constructor
A(double v):_value(v) {}
//copy constructor
A(const A &o):_value(o._value) {}
//move constructor
A(A &&o):_value(o._value) { o._value = 0; }
double value() const { return _value; }
};
class B //second class
{
private:
int _value;
public:
//default constructor
B():_value(0) {}
//constructor
B(int v):_value(v) {}
//copy constructor
B(const B &o):_value(o._value) {}
//move constructor
B(B &&o):_value(o._value) { o._value = 0; }
//some public member
int value() const { return _value; }
};
class Erasure //the type erasure
{
private:
class Interface //interface of the holder
{
public:
virtual double value() const = 0;
};
//holder template - implementing the interface
template<typename T> class Holder:public Interface
{
public:
T _object;
public:
//construct by copying o
Holder(const T &o):_object(o) {}
//construct by moving o
Holder(T &&o):_object(std::move(o)) {}
//copy constructor
Holder(const Holder<T> &o):_object(o._object) {}
//move constructor
Holder(Holder<T> &&o):_object(std::move(o._object)) {}
//implements the virtual member function
virtual double value() const
{
return double(_object.value());
}
};
Interface *_ptr; //pointer to holder
public:
//construction by copying o
template<typename T> Erasure(const T &o):
_ptr(new Holder<T>(o))
{}
//construction by moving o
template<typename T> Erasure(T &&o):
_ptr(new Holder<T>(std::move(o)))
{}
//delegate
double value() const { return _ptr->value(); }
};
int main(int argc,char **argv)
{
A a(100.2344);
B b(-100);
Erasure g1(std::move(a));
Erasure g2(b);
return 0;
}
As a compiler I use gcc 4.7 on a Debian testing system. Assuming the code is stored in a file named terasure.cpp the build leads to the following error message
$> g++ -std=c++0x -o terasure terasure.cpp
terasure.cpp: In instantiation of ‘class Erasure::Holder<B&>’:
terasure.cpp:78:45: required from ‘Erasure::Erasure(T&&) [with T = B&]’
terasure.cpp:92:17: required from here
terasure.cpp:56:17: error: ‘Erasure::Holder<T>::Holder(T&&) [with T = B&]’ cannot be overloaded
terasure.cpp:54:17: error: with ‘Erasure::Holder<T>::Holder(const T&) [with T = B&]’
terasure.cpp: In instantiation of ‘Erasure::Erasure(T&&) [with T = B&]’:
terasure.cpp:92:17: required from here
terasure.cpp:78:45: error: no matching function for call to ‘Erasure::Holder<B&>::Holder(std::remove_reference<B&>::type)’
terasure.cpp:78:45: note: candidates are:
terasure.cpp:60:17: note: Erasure::Holder<T>::Holder(Erasure::Holder<T>&&) [with T = B&]
terasure.cpp:60:17: note: no known conversion for argument 1 from ‘std::remove_reference<B&>::type {aka B}’ to ‘Erasure::Holder<B&>&&’
terasure.cpp:58:17: note: Erasure::Holder<T>::Holder(const Erasure::Holder<T>&) [with T = B&]
terasure.cpp:58:17: note: no known conversion for argument 1 from ‘std::remove_reference<B&>::type {aka B}’ to ‘const Erasure::Holder<B&>&’
terasure.cpp:54:17: note: Erasure::Holder<T>::Holder(const T&) [with T = B&]
terasure.cpp:54:17: note: no known conversion for argument 1 from ‘std::remove_reference<B&>::type {aka B}’ to ‘B&’
It seems that for Erasure g2(b); the compiler still tries to use the move constructor. Is this intended behavior of the compiler? Do I missunderstand something in general with the type erasure pattern? Does someone have an idea how to get this right?
As evident from the compiler errors, the compiler is trying to instantiate your Holder class for T = B&. This means that the class would store a member of a reference type, which gives you some problems on copy and such.
The problem lies in the fact that T&& (for deduced template arguments) is an universal reference, meaning it will bind to everything. For r-values of B it will deduce T to be B and bind as an r-value reference, for l-values it will deduce T to be B& and use reference collapsing to interpret B& && as B& (for const B l-values it would deduce T to be const B& and do the collapsing). In your example b is a modifiable l-value, making the constructor taking T&& (deduced to be B&) a better match then the const T& (deduced to be const B&) one. This also means that the Erasure constructor taking const T& isn't really necessary (unlike the one for Holder due to T not being deduced for that constructor).
The solution to this is to strip the reference (and probably constness, unless you want a const member) from the type when creating your holder class. You should also use std::forward<T> instead of std::move, since as mentioned the constructor also binds to l-values and moving from those is probably a bad idea.
template<typename T> Erasure(T&& o):
_ptr(new Holder<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(std::forward<T>(o))
{}
There is another bug in your Erasure class, which won't be caught by the compiler: You store your Holder in a raw pointer to heap allocated memory, but have neither custom destructor to delete it nor custom handling for copying/moving/assignment (Rule of Three/Five). One option to solve that would be to implement those operations (or forbid the nonessential ones using =delete). However this is somewhat tedious, so my personal suggestion would be not to manage memory manually, but to use a std::unique_ptr for memory management (won't give you copying ability, but if you want that you first need to expand you Holder class for cloning anyways).
Other points to consider:
Why are you implementing custom copy/move constructors for Erasure::Holder<T>, A and B? The default ones should be perfectly fine and won't disable the generation of a move assignment operator.
Another point is that Erasure(T &&o) is problematic in that it will compete with the copy/move constructor (T&& can bind to Èrasure& which is a better match then both const Erasure& and Erasure&&). To avoid this you can use enable_if to check against types of Erasure, giving you something similar to this:
template<typename T, typename Dummy = typename std::enable_if<!std::is_same<Erasure, std::remove_reference<T>>::value>::type>
Erasure(T&& o):
_ptr(new Holder<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(std::forward<T>(o))
{}
Your problem is that the type T is deduced to be a reference by your constructor taking a universal reference. You want to use something along the lines of this:
#include <type_traits>
class Erasure {
....
//construction by moving o
template<typename T>
Erasure(T &&o):
_ptr(new Holder<typename std::remove_reference<T>::type>(std::forward<T>(o)))
{
}
};
That is, you need to remove any references deduced from T (and probably also any cv qualifier but the correction doesn't do that). and then you don't want to std::move() the argument o but std::forward<T>() it: using std::move(o) could have catastrophic consequences in case you actually do pass a non-const reference to a constructor of Erasure.
I didn't pay too much attention to the other code put as far as I can tell there also a few semantic errors (e.g., you either need some form of reference counting or a form of clone()int the contained pointers, as well as resource control (i.e., copy constructor, copy assignment, and destructor) in Erasure.
I have a class that 'remembers' a reference to some object (e.g. an integer variable). I can't have it reference a value that's destructed immediately, and I'm looking for a way to protect the users of my class from doing so by accident.
Is an rvalue-reference overload a good way to prevent a temporary to be passed in?
struct HasRef {
int& a;
HasRef(int& a):a(a){}
void foo(){ a=1; }
};
int main(){
int x=5;
HasRef r1(x);
r1.foo(); // works like intended.
HasRef r2(x+4);
r2.foo(); // dereferences the temporary created by x+4
}
Would a private rvalue overload do?
struct HasRef {
int& a;
HasRef( int& a ):a(a){}
void foo(){ a=1; }
private:
HasRef( int&& a );
};
... HasRef r2(x+1); // doesn't compile => problem solved?
Are there any pitfalls I didn't see?
If you have to store a const reference to some instance of type B into your class A, then surely you want to be ensured, that lifetime of A instance will be exceeded by the lifetime of B instance:
B b{};
A a1{b}; // allowed
A a2{B{}}; // should be denied
B const f() { return B{}; } // const result type may make sense for user-defined types
A a3{f()}; // should also be denied!
To make it possible you should explicitly to = delete; all the constructor overloadings, which can accept rvalues (both const && and &&). For this to achieve you should just to = delete; only const && version of constructor.
struct B {};
struct A
{
B const & b;
A(B const & bb) : b(bb) { ; } // accepts only `B const &` and `B &`
A(B const &&) = delete; // prohibits both `B &&` and `B const &&`
};
This approach allows you to prohibit passing to the constructor all kinds of rvalues.
This also works for built-in scalars. For example, double const f() { return 0.01; }, though it cause a warning like:
warning: 'const' type qualifier on return type has no effect [-Wignored-qualifiers]
it still can has effect if you just = delete; only && version of constructor:
struct A
{
double const & eps;
A(double const & e) : eps(e) {} // binds to `double const &`, `double &` AND ! `double const &&`
A(double &&) = delete; // prohibit to binding only to `double &&`, but not to `double const &&`
};
double const get_eps() { return 0.01; }
A a{0.01}; // hard error
A a{get_eps()}; // no hard error, but it is wrong!
For non-conversion constructors (i.e. non-unary) there is an issue: you may have to provide = delete;-d versions for all the combinatorically possible versions of constructors as follows:
struct A
{
A(B const &, C const &) {}
A(B const &&, C const &&) = delete;
// and also!
A(B const &, C const &&) = delete;
A(B const &&, C const &) = delete;
};
to prohibit mixed-cases like:
B b{};
A a{b, C{}};
Ignoring the fact the code isn't valid and just answering the question about the private overload...
In C++11 I would prefer a deleted function to a private function. It's a bit more explicit that you really can't call it (not even if you're a member or friend of the class.)
N.B. if the deleted constructor is HasRef(int&&)=delete it will not be chosen here:
int i;
HasRef hr(std::forward<const int>(i));
With an argument of type const int&& the HasRef(const int&) constructor would be used, not the HasRef(int&&) one. In this case it would be OK, because i really is an lvalue, but in general that might not be the case, so this might be one of the very rare times when a const rvalue reference is useful:
HasRef(const int&&) = delete;
That shouldn't compile. A good C++ compiler (or really almost any C++ compiler that I've ever seen) will stop that from happening.
I'm guessing you're compiling in MSVS. In that case, turn off language extensions and you should get an error.
Otherwise, not that even marking the reference const extends the lifetime of the temporary until the constructor finishes. After that, you'll refer to an invalid object.
class my_class
{
...
my_class(my_class const &) = delete;
...
};
What does = delete mean in that context?
Are there any other "modifiers" (other than = 0 and = delete)?
Deleting a function is a C++11 feature:
The common idiom of "prohibiting copying" can now be expressed
directly:
class X {
// ...
X& operator=(const X&) = delete; // Disallow copying
X(const X&) = delete;
};
[...]
The "delete" mechanism can be used for any function. For example, we
can eliminate an undesired conversion like this:
struct Z {
// ...
Z(long long); // can initialize with a long long
Z(long) = delete; // but not anything less
};
= 0 means that a function is pure virtual and you cannot instantiate an object from this class. You need to derive from it and implement this method
= delete means that the compiler will not generate those constructors for you. AFAIK this is only allowed on copy constructor and assignment operator. But I am not too good at the upcoming standard.
This excerpt from The C++ Programming Language [4th Edition] - Bjarne Stroustrup book talks about the real purpose behind using =delete:
3.3.4 Suppressing Operations
Using the default copy or move for a class in a hierarchy is typically
a disaster: given only a pointer to a base, we simply don’t know what
members the derived class has, so we can’t know how to copy
them. So, the best thing to do is usually to delete the default copy
and move operations, that is, to eliminate the default definitions of
those two operations:
class Shape {
public:
Shape(const Shape&) =delete; // no copy operations
Shape& operator=(const Shape&) =delete;
Shape(Shape&&) =delete; // no move operations
Shape& operator=(Shape&&) =delete;
˜Shape();
// ...
};
Now an attempt to copy a Shape will be caught by the compiler.
The =delete mechanism is general, that is, it can be used to suppress any operation
Are there any other "modifiers" (other than = 0 and = delete)?
Since it appears no one else answered this question, I should mention that there is also =default.
https://learn.microsoft.com/en-us/cpp/cpp/explicitly-defaulted-and-deleted-functions#explicitly-defaulted-functions
The coding standards I've worked with have had the following for most of class declarations.
// coding standard: disallow when not used
T(void) = delete; // default ctor (1)
~T(void) = delete; // default dtor (2)
T(const T&) = delete; // copy ctor (3)
T(const T&&) = delete; // move ctor (4)
T& operator= (const T&) = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)
If you use any of these 6, you simply comment out the corresponding line.
Example: class FizzBus require only dtor, and thus do not use the other 5.
// coding standard: disallow when not used
FizzBuzz(void) = delete; // default ctor (1)
// ~FizzBuzz(void); // dtor (2)
FizzBuzz(const FizzBuzz&) = delete; // copy ctor (3)
FizzBuzz& operator= (const FizzBuzz&) = delete; // copy assig (4)
FizzBuzz(const FizzBuzz&&) = delete; // move ctor (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign (6)
We comment out only 1 here, and install the implementation of it else where (probably where the coding standard suggests). The other 5 (of 6) are disallowed with delete.
You can also use '= delete' to disallow implicit promotions of different sized values ... example
// disallow implicit promotions
template <class T> operator T(void) = delete;
template <class T> Vuint64& operator= (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;
A deleted function is implicitly inline
(Addendum to existing answers)
... And a deleted function shall be the first declaration of the function (except for deleting explicit specializations of function templates - deletion should be at the first declaration of the specialization), meaning you cannot declare a function and later delete it, say, at its definition local to a translation unit.
Citing [dcl.fct.def.delete]/4:
A deleted function is implicitly inline. ( Note: The one-definition
rule
([basic.def.odr])
applies to deleted definitions. — end note ] A deleted definition
of a function shall be the first declaration of the function or, for
an explicit specialization of a function template, the first
declaration of that specialization. [ Example:
struct sometype {
sometype();
};
sometype::sometype() = delete; // ill-formed; not first declaration
— end example )
A primary function template with a deleted definition can be specialized
Albeit a general rule of thumb is to avoid specializing function templates as specializations do not participate in the first step of overload resolution, there are arguable some contexts where it can be useful. E.g. when using a non-overloaded primary function template with no definition to match all types which one would not like implicitly converted to an otherwise matching-by-conversion overload; i.e., to implicitly remove a number of implicit-conversion matches by only implementing exact type matches in the explicit specialization of the non-defined, non-overloaded primary function template.
Before the deleted function concept of C++11, one could do this by simply omitting the definition of the primary function template, but this gave obscure undefined reference errors that arguably gave no semantic intent whatsoever from the author of primary function template (intentionally omitted?). If we instead explicitly delete the primary function template, the error messages in case no suitable explicit specialization is found becomes much nicer, and also shows that the omission/deletion of the primary function template's definition was intentional.
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t);
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
//use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}
However, instead of simply omitting a definition for the primary function template above, yielding an obscure undefined reference error when no explicit specialization matches, the primary template definition can be deleted:
#include <iostream>
#include <string>
template< typename T >
void use_only_explicit_specializations(T t) = delete;
template<>
void use_only_explicit_specializations<int>(int t) {
std::cout << "int: " << t;
}
int main()
{
const int num = 42;
const std::string str = "foo";
use_only_explicit_specializations(num); // int: 42
use_only_explicit_specializations(str);
/* error: call to deleted function 'use_only_explicit_specializations'
note: candidate function [with T = std::__1::basic_string<char>] has
been explicitly deleted
void use_only_explicit_specializations(T t) = delete; */
}
Yielding a more more readable error message, where the deletion intent is also clearly visible (where an undefined reference error could lead to the developer thinking this an unthoughtful mistake).
Returning to why would we ever want to use this technique? Again, explicit specializations could be useful to implicitly remove implicit conversions.
#include <cstdint>
#include <iostream>
void warning_at_best(int8_t num) {
std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}
template< typename T >
void only_for_signed(T t) = delete;
template<>
void only_for_signed<int8_t>(int8_t t) {
std::cout << "UB safe! 1 byte, " << +t << "\n";
}
template<>
void only_for_signed<int16_t>(int16_t t) {
std::cout << "UB safe! 2 bytes, " << +t << "\n";
}
int main()
{
const int8_t a = 42;
const uint8_t b = 255U;
const int16_t c = 255;
const float d = 200.F;
warning_at_best(a); // 42
warning_at_best(b); // implementation-defined behaviour, no diagnostic required
warning_at_best(c); // narrowing, -Wconstant-conversion warning
warning_at_best(d); // undefined behaviour!
only_for_signed(a);
only_for_signed(c);
//only_for_signed(b);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = unsigned char]
has been explicitly deleted
void only_for_signed(T t) = delete; */
//only_for_signed(d);
/* error: call to deleted function 'only_for_signed'
note: candidate function [with T = float]
has been explicitly deleted
void only_for_signed(T t) = delete; */
}
= delete is a feature introduce in C++11. As per =delete it will not allowed to call that function.
In detail.
Suppose in a class.
Class ABC{
Int d;
Public:
ABC& operator= (const ABC& obj) =delete
{
}
};
While calling this function for obj assignment it will not allowed. Means assignment operator is going to restrict to copy from one object to another.
New C++0x standard. Please see section 8.4.3 in the N3242 working draft
This is new thing in C++ 0x standards where you can delete an inherited function.
A small example to summarize some common usages:
class MyClass
{
public:
// Delete copy constructor:
// delete the copy constructor so you cannot copy-construct an object
// of this class from a different object of this class
MyClass(const MyClass&) = delete;
// Delete assignment operator:
// delete the `=` operator (`operator=()` class method) to disable copying
// an object of this class
MyClass& operator=(const MyClass&) = delete;
// Delete constructor with certain types you'd like to
// disallow:
// (Arbitrary example) don't allow constructing from an `int` type. Expect
// `uint64_t` instead.
MyClass(uint64_t);
MyClass(int) = delete;
// "Pure virtual" function:
// `= 0` makes this is a "pure virtual" method which *must* be overridden
// by a child class
uint32_t getVal() = 0;
}
TODO:
I still need to make a more thorough example, and run this to show some usages and output, and their corresponding error messages.
See also
https://www.stroustrup.com/C++11FAQ.html#default - section "control of defaults: default and delete"
Please, could someone explain in plain English what is "Extending move semantics to *this"? I am referring to this proposal. All what am looking for is what is that & why do we need that. Note that I do understand what an rvalue reference is in general, upon which move semantics is built. I am not able to grasp what such an extension adds to rvalue references!
The ref-qualifier feature (indicating the type of *this) would allow you to distinguish whether a member function can be called on rvalues or lvalues (or both), and to overload functions based on that. The first version gives some rationale in the informal part:
Prevent surprises:
struct S {
S* operator &() &; // Selected for lvalues only
S& operator=(S const&) &; // Selected for lvalues only
};
int main() {
S* p = &S(); // Error!
S() = S(); // Error!
}
Enable move semantics:
class X {
std::vector<char> data_;
public:
// ...
std::vector<char> const & data() const & { return data_; }
std::vector<char> && data() && { return data_; } //should probably be std::move(data_)
};
X f();
// ...
X x;
std::vector<char> a = x.data(); // copy
std::vector<char> b = f().data(); // move
For example, you can overload operators as free functions with rvalue references if you wish:
Foo operator+(Foo&& a, const Foo& b)
{
a += b;
return std::move(a);
}
To achieve the same effect with a member function, you need the quoted proposal:
Foo Foo::operator+(const Foo& b) && // note the double ampersand
{
*this += b;
return *this;
}
The double ampersand says "this member function can only be called on rvalues".
Whether or not you must explicitly move from *this in such a member function is discussed here.