Consider the following code :
template <class T>
T average(T *atArray, int nNumValues)
{
T tSum = 0;
for (int nCount = 0; nCount < nNumValues; nCount++)
{
tSum += atArray[nCount];
}
tSum = tSum / nNumValues;
return tSum;
}
And the following question on it :
Which of the following statements about the class/type T must be true in order for the code to compile and run without crashing?
It must be some kind of numeric type
Must have < operator-defined
Must have the [ ] access operator-defined
Must have copy constructor and assignment operator
My Thoughts:
I think it could be something apart from numeric type but it will need to have the operator + and / well defined.
point 2 seems incorrect as < has no relation with / and + operator
same with point 3 and point 4.
Although I am not sure about my reasoning above.
No - It could for example do implicit conversions to/from a numeric type and have operator+= defined.
No - That operator is not used on a T
No - That operator is not used on a T, only a T* and pointer types all have this operator defined.
No - The copy constructor can be deleted and the question says "and" so the answer is no. It needs some kind of assignment operator though.
Consider this class, especially constructed to be able to answer no to as many questions as possible:
struct foo {
foo() = default;
foo(int) {} // implicit conversion from int
foo(const foo&) = delete; // no copy constructor
foo(foo&&) = default; // but move constructor
foo& operator=(const foo&) = delete; // no copy assignment
foo& operator=(foo&&) = delete; // no move assignment
foo& operator=(int) { return *this; }; // but assignment from int
foo& operator+=(const foo&) { return *this; } // for tSum += atArray[nCount];
operator int() const { return 1; } // implicit conversion to int
};
And it would work fine with the function template:
int main() {
foo arr[2];
auto x = average(arr, 2);
}
A class type with sufficient overloaded operators (like std::complex<double>) could work.
The < is only used on int expressions, not involving T.
The atArray[nCount] expression uses a T* pointer, not an expression with type T, so it has the built-in meaning.
Technically no, since fundamental types don't have constructors or assignment operators at all, just similar semantics. Also, technically every class type "has" a copy constructor and assignment operator, even if deleted. Another issue: T could be a class with normal assignment operator, deleted copy constructor, and public non-deleted move constructor.
Probably the intended answer is #4.
The actual requirements on T are:
A variable of type T can be copy-initialized from an int, or from a null pointer constant. (0 is a special expression which can do two different things...)
T is a numeric type, or an enumeration or class type with valid operator functions supporting expressions:
a += b and a = b, where a and b are lvalues of type T
a / n, where a is an lvalue of type T and n is an lvalue of type int
T is move-constructible.
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"
Suppose I have the code below, where the copy-assignment operator is deleted and an int-assignment operator is emplaced alongside an int-access operator (not marked with the explicit keyword). The assignment of b to a only works when explicitly casting to int as below, while a simple a = b; generates a compilation error of E1776 function "OverloadTest::operator=(const OverloadTest &)" cannot be referenced -- it is a deleted function. Is there any explanation for this behavior, which ought to take advantage of the explicit deletion and the implementation of implicit operators? Using MSVC++ 14.15.
class OverloadTest
{
int i;
public:
OverloadTest(int i) : i(i)
{
}
OverloadTest operator=(const OverloadTest &) = delete;
int operator=(const int &other)
{
i = other;
return i;
}
operator int()
{
return i;
}
};
int main()
{
OverloadTest a(1), b(2);
a = b; // E1776
a = (int)b; // OK
int (OverloadTest::* e)(const int &) = &OverloadTest::operator=;
(a.*(&OverloadTest::operator=))(b); // E0299
(a.*e)(b); // OK
return 0;
}
Actually it is not really clear why you expected something else as this is just how deleting a method is supposed to work. From cppreference (emphasize mine):
If, instead of a function body, the special syntax = delete ; is used,
the function is defined as deleted. Any use of a deleted function
is ill-formed (the program will not compile).
By writing
OverloadTest operator=(const OverloadTest &) = delete;
you do define the operator, but calling it will make your code ill-formed. I find it difficult to answer more than that, because your example is rather academic. You can make a=b; work if you simple do not declare the operator=(const OverloadTest&) at all. However, note that then the compiler generated operator= will be used to evaluate a=b;. Though as your class only has the int member you actually cannot tell the difference between calling that operator or your conversion followed by operator=(int). Hope that helps.
Is it possible for assignment operator to be deduced as a special case of member function template?
For example, I have a class template with one bool parameter and want to implement assign operation regardless any particular value of the template argument.
#include <iostream>
template<bool sw>
struct A {
A() {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
template<bool input_sw>
A & operator = (const A<input_sw> &a) {
std::cout << __PRETTY_FUNCTION__ << '\n';
return *this;
}
};
int main()
{
A<true> a;
A<true> b;
a = b;
}
In the code snippet above clang and gcc-compiled binaries print out nothing about assignment -- as far as I can tell default assignment is generated here despite possibility to deduce it from the template.
It is correct for them to not print anything out. What is happening is that there is an implicit copy assignment operator created for A<true> with this signature:
A<true>& operator=(const A<true>& );
Thus, when we do overload resolution, there are two viable candidates:
A<true>& operator=(const A<true>& ); // implicit copy assignment
A<true>& operator=(const A<input_sw>& ); // [ with input_sw = true ]
Both operators take the same argument (const A<true>&), so they are equivalently good candidates from this perspective. But non template functions are preferred to function template specializations, which makes the implicit copy assignment operator the best viable candidate.
Now, consider an alternative. What if you declared your operator template this way:
template<bool input_sw>
A & operator =(A<input_sw> &a) { ... }
That is, not const. This isn't good practice, and I'm presenting this solely for illustrative purposes. In this case, our two candidates for a=b are:
A<true>& operator=(const A<true>& ); // implicit copy assignment
A<true>& operator=(A<input_sw>& ); // [ with input_sw = true ]
Now the two candidates do not take the same argument. Our operator template takes a reference to non-const. In the case of two references, the one referring to the last cv-qualified type is preferred, which in this case would be the operator. Drop that const, and now your function is preferred, and you will see something printed.
C++ is the best.
Given the following code:
template <typename T>
struct Wrapper {
T value;
Wrapper(T val) : value(val) {}
}
int main() {
Wrapper<int> x(42);
int y = x; // need solution for y = x.value
return 0;
}
Is there a way to implement the assignment
int y = x;
so that it means y = x.value .
I know that overloading the assignment operator itself is not possible because it applies to the object on the left side of the assignment and friend function with two arguments is not allowed by the standard.
If this is not possible by overloading any other operator, or by using some special tricks, how would you implement this, except by invoking the get method provided by the Wrapper class such as:
int y = x.get();
Why not just provide an implicit conversion to T
operator T() { return value; }
This will cause the assignment to function because the compiler will attempt to convert the right side of the assignment to T. The implicit conversion will allow that to succeed
Note that this will cause other conversions to work besides assignment. For example it will now be possible to pass Wrapper<T> instances as T parameters. That may or may not work for your particular scenario
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"