What's the difference between explicit and implicit assignment in C++ - c++

int value = 5; // this type of assignment is called an explicit assignment
int value(5); // this type of assignment is called an implicit assignment
What is the difference between those, if any, and in what cases do explicit and implicit assignment differ and how?
http://weblogs.asp.net/kennykerr/archive/2004/08/31/Explicit-Constructors.aspx
EDIT: I actually just found this article, which makes the whole thing a lot clearer... and it brings up another question, should you (in general) mark constructors taking a single parameter of a primitive type - numeric/bool/string - as explicit and leave the rest as they are (of course keeping watch for gotchas such as constructors like (int, SomeType = SomeType())?

Neither of these is an assignment of any kind -- they're both initialization. The first uses copy initialization, and the second direct initialization. (FWIW, I'm pretty sure I've never heard the terms "explicit assignment" or "implicit assignment" before).
Edit: (Mostly in response to Nathan's comment):
Here's a corrected version of the code from your comment:
#include <iostream>
struct Foo {
Foo() {
std::cout << "Foo::ctor()" << std::endl;
}
Foo(Foo const& copy) {
std::cout << "Foo::cctor()" << std::endl;
}
Foo& operator=(Foo const& copy) {
std::cout << "foo::assign()" << std::endl;
return *this;
}
};
int main(int, const char**) {
Foo f;
Foo b(f);
Foo x = b;
return 0;
}
The result from running this should be:
Foo::ctor()
Foo::cctor()
Foo::cctor()
If you run it and get an foo::assign(), throw your compiler away and get one that works (oh, and let us know what compiler it is that's so badly broken)!

They differ if a class has a constructor marked 'explicit'. Then, one of these does not work.
Otherwise, no difference.

Only the first one is an assignment. They are both initialization.
Edit: actually, I'm wrong. Neither are assignment.

Related

Is C++17 copy elision applied to placement new?

I am writing a generic interface for scripting languages and need to call functions that return an object and put the resulting object in a space provided by the scripting language.
I have tested with clang++ on Mac OS and the following seems to do the copy/move elision:
class T {
public:
T() {}
T(const T &t) {
std::cout << "Copy" << std::endl;
}
T(T&&t) : x(t.x) {
std::cout << "Move" << std::endl;
}
private:
int x = 3;
};
T h()
{
return T();
}
void buildTInto(void *p)
{
new (p) T(h());
}
int main(int argc, char *argv[])
{
alignas(T) char space[sizeof(T)];
buildTInto(space);
}
Running this code does not print anything.
Reading the cpp reference description of copy elision seems to indicate this is the correct behavior, given the example they give which only calls one constructor:
T x = T(T(f())); // only one call to default constructor of T, to initialize x
I think my code is very much similar as without copy elision, the move constructor would be called (just like the T(T(f())) would call two move construction).
Not being a spec lawyer, I am curious if my understanding is correct and I wonder if all C++17 capable compilers do this correctly?
Guaranteed elision (aka: C++17's definition of prvalues) applies to the initialization of any object of type T with a prvalue of the same type. That includes objects created by new expressions, including placement new forms.
Indeed, you don't even have to repeat the type at the point of invoking the new expression: new(p) auto(h()); ensures the obligate condition for guaranteed elision.

return const value prevent move semantics

I am a beginner in cpp so excuse me for this question.
I was reading that returning const val prevents move semantics.
therefore I dont understand why the following code is compiled and works normally. is it because only temporary object is being created? in what cases the move semantics cannot being done? thank you in advance!
#include <iostream>
using namespace std;
const string foo()
{
return string("hello");
}
int main()
{
string other = std::move(foo());
}
std::move is just a unconditional cast to rvalue. In your case the return value of std::move(foo()) was const std::string&&. And because move constructor does not take const argument, copy constructor was called instead.
struct C {
C() { std::cout << "constructor" << std::endl; }
C(const C& other) { std::cout << "copy constructor" << std::endl; }
C(C&& other) { std::cout << "move constructor" << std::endl; }
};
const C get() {
return C();
}
int main() {
C c(std::move(get()));
return 0;
}
I was reading that returning const val prevents move semantics. therefore I dont understand why the following code is compiled and works normally.
When move semantics are prevented by some mechanism, this doesn't necessarily mean that the code doesn't compile. Often, it compiles happily, but an expected move construction turns out to be a copy instead.
Example: a type has a user provided copy ctor, which disables compiler-generated move ctors. When we think we move-construct, we don't.
struct Test {
Test() = default;
Test(const Test&) {}
};
Test t1;
Test t2{std::move(t1)}; // Copies!
in what cases the move semantics cannot being done?
Coming to your example, something that is const-qualified can't be used to move-construct another object in any meaningful way. Move construction makes sense when resources can be easily transferred, but const-ness prevents that. Example: a type has compiler-generate move and copy constructors, but we can't move-construct from a const instance.
struct Test {
Test() = default;
};
const Test t1;
Test t2{std::move(t1)}; // Copies!
Besides, it doesn't make sense to move something that is returned by a function by value:
string other = std::move(foo());
When foo() returns by value, you can move-construct from it, unless the return type is const. Hence, to enable move-construction of other:
std::string foo();
string other = foo();
std::move doesn't actually move anything. It is just an "rvalue cast". You cast something to rvalue, and the move constructor / move assignment operator does the actual moving if possible. "If possible" part is the key. In your example the return value is already an rvalue, so std::move literally does nothing. You may even get warnings like "nothing is moved". That is because the move constructor of std::string takes an argument of type std::string&& not const std::string&&. Because of that, the copy constructor is called.

Is it best practice to use a initializer_list in assignment

I have read (Bjarne Stroustrup, The C++ Programming Language, 6.3.5) about using initializer_list when initializing a variable, so that you don't have a narrowing conversion. Bjarne recommends only using direct-list-initialization :
X a1 {v};
X a2 = {v};
X a3 = v;
X a4(v);
Of these, only the first can be used in every context, and I strongly
recommend its use. It is clearer and less error-prone than the
alternatives.
Why does Bjarne only recommend the first one?
Why isn't it recommended to do initializer_list in assignment (rather than initialization)? Or is it just implied that you should do that?
a1 = {v};
Here is an example of what I am asking about? Why is initializer_list not recommended for assignment (from what I can tell) but it is recommended for initialization? It seems like it is beneficial by reducing potential narrowing conversions on accident.
char c;
int toobig = 256;
c = 256; //no error, narrowing occurs
c = { toobig }; //narrowing conversion, error
Initializer lists are usually recommended and overcome a syntactic trap called 'The Most Vexxing Parse'.
std::vector<int> v();
Inside a function this looks like variable declaration but it's a declaration of a function v taking no arguments returning a std::vector<int>.
OK so std::vector<int> v; fixes that one but in a template a parameter may be a built in type where int x; leaves x uninitialized but int x{}; (value) initializes it to zero.
In modern C++ with copy elision and a couple of syntatic rules, there aren't that many ways to accidentally create temporary copies in a variable declaration.
But initializer-lists do clear up a couple of anomalies and are to be recommended.
Overloading in C++ is quite keen and there remain reasons to sometimes use () to call the appropriate constructor.
For example std::vector<T> can take a initializer-list of values and create an array containing that list. Great.
It can also take count and value arguments and create an array of count copies of `value'. Also great.
But if the size type is compatible with the value-type (T) you can still get a surprise!
#include <iostream>
#include <vector>
template<typename T>
void dump_vector(const std::string& tag,const std::vector<T>& vec);
int main() {
std::vector<int> v1(5,20);
std::vector<int> v2{5,20};
std::vector<std::string> v3(5,"Hi!");
std::vector<std::string> v4{5,"Hi!"};
dump_vector("v1",v1);
dump_vector("v2",v2);
dump_vector("v3",v3);
dump_vector("v4",v4);
return 0;
}
template<typename T>
void dump_vector(const std::string& tag,const std::vector<T>& vec){
std::cout<< tag << "={ ";
auto begin=vec.begin();
auto end=vec.end();
for(auto it{begin};it!=end;++it){
if(it!=begin){
std::cout<<", ";
}
std::cout<<*it;
}
std::cout << " }\n";
}
Expected output:
v1={ 20, 20, 20, 20, 20 }
v2={ 5, 20 }
v3={ Hi!, Hi!, Hi!, Hi!, Hi! }
v4={ Hi!, Hi!, Hi!, Hi!, Hi! }
The versions with () and {} did different things for std::vector<int> but landed on the same constructor for std::vector<std::string>.
This isn't a new or unique problem. C++ uses types heavily in selecting an overload and when there's a bunch of candidates different template instantiations might make an unexpected choice!
I'd say initializer-list is now preferred. But when a constructor that itself takes in initializer list exists, you may need to be explict if you don't want to hit it.
Also worth a read http://read:%20https://herbsutter.com/2013/05/09/gotw-1-solution/
Take this as example
#include <iostream>
struct foo
{
explicit foo(int)
{
std::cout << "[+] c'tor called\n";
}
foo(const foo&)
{
std::cout << "[+] copy c'tor called\n";
}
};
int main()
{
std::cout << "\ncreating object a\n";
foo a = foo{1};
std::cout << "\n\ncreating object b\n";
foo b{1};
}
Compiling with g++ main.cpp --std=c++11 -fno-elide-constructors
Output:
creating object a
[+] c'tor called
[+] copy c'tor called
creating object b
[+] c'tor called
In the first a temporary is created and then a is created by calling the copy constructor.
Whereas in second example the object is directly created.
There are few cases where you have to use () syntax instead of {}.
Refer to Scott Meyers-Effective Modern C++ (item 7)`

Reference binding and the copy constructor/move constructor

If I have a function like so:
int foo(std::vector<int>* integer1ArrayIn, * integer2ArrayIn) {
std::vector<int>& integer1Array = *integer1ArrayIn;
std::vector<int>& integer2Array = *integer2ArrayIn;
}
Will the reference integer1Array be calling a copy constructor/move constructor to copy over the elements of the passed in parameter?
Does binding a reference to a dereferenced pointer call the copy constructor?
In what circumstances does reference binding invoke a copy constructor?
Can someone explain what happens as this code executes in memory?
Thank you
No.
No, but it would crash badly if it was nullptr. Consider passing by reference whenever a parameter HAS to be there, and pass by pointer when a parameter MIGHT be there (but always verify for nullptr too!).
Whenever the l-value (in this case integer1Array and integer2Array) are pointers or references, it will never call copy/move constructor.
if you had std::vector integer1Array = *integer1ArrayIn it would effectively make a copy.
You can use Jonas's answer to play around with and see for yourself :)
1) No, there are no copies made. You can test it with a small program, like this.
#include <iostream>
struct foo
{
foo() { std::cout << "Constructor" << std::endl; }
foo(const foo&) { std::cout << "Copy constructor" << std::endl; }
foo& operator=(const foo&) { std::cout << "Copy assignment operator" << std::endl; }
};
int main() {
foo* A = new foo;
foo& B = *A;
delete A;
}
2) Beware of nullptrs! Otherwise all is fine.
3) Never (see answer by AlexG)
4) Not sure what you mean by "code executes in memory", since code is not executed in memory. If you mean what happens to the memory when the program is executed, then that's another story

Understanding the process of initialization

This is my attempt to understand how class initialization works. I'm not sure about everything, and that is why I'm asking this question. This is what I believe happens when we do the following:
T t = u;
Constructs an object of type T from u. This then becomes:
T t = T(u);
Calls the copy-constructor:
T t( T(u) );
Okay the second is the part I'm not understanding. I read somewhere that T t = u is made into T t(T(u)). But if that is true why doesn't this print "copy-constructor":
struct T
{
template <class U>
T(U) {
std::cout << "constructs an object of type T...\n";
}
T(T const &)
{
std::cout << "copy-constructor";
}
T& operator=(T const &)
{
std::cout << "assignment operator"; return *this;
}
T() = default;
};
int main()
{
T t(T(5));
}
Actually, all this does is print "constructs an object of type T". Why isn't the copy-constructor called here? T(5) can be made into an object of type T const & which is passed into the constructor of T so shouldn't the appropriate constructor be called.
I'd really like some insight to this. I've been trying to understand this for a while.
There's a special rule for this situation. The compiler is allowed to skip the copy constructor, even though it has side effects, provided the expression with the copy constructor would have been legal. So
T t = u;
is, as you say, equivalent to
T t(T(u));
The compiler skips the copy constructor and treats this as
T t(u);
One situation where this would not be allowed would be a class with a private copy constructor. In that case, T t(T(u)); would not be legal, and the compiler would not be allowed to make it legal by skipping the copy constructor.