about vs/g++ with copy-constructor - c++

I'm trying to compile the code in Visual Studio (2008) and g++.
In vs2008 it's successful, but in g++ it reported error.
if add const,
test(const test &source):a(source.a) {}
g++ will compiled succeed.
I kown that test aa = 2; will create a temporary object and call copy-constructor.
temporary object cannot bind to a non-const reference
so,why the vs2008 can compiled it succeed?
class test{
public:
test():a(1) {}
test(int num):a(num) {}
test(test &source):a(source.a) {}
private:
int a;
};
int main(){
test aa = 2;
return 0;
}

VS has a non-standard extension that allows it, unfortunately.
There's a compiler flag to disable extensions, but last I checked it also makes it impossible to use the standard library. Your best bet is to keep the Warning Level on 4 (though this particular situation gets no warning), and check your compilations with multiple compilers when possible.

Related

Detect use after move during compilation?

The clang-tidy static analyzer detects uses of variables after being moved.
class a_class {
std::unique_ptr<int> p_;
public:
auto p() -> auto& {return p_;}
void f() const {}
};
int main() {
auto aa = a_class{};
[[maybe_unused]] auto bb = std::move(aa);
aa.f();
}
error: Method called on moved-from object 'aa' [clang-analyzer-cplusplus.Move,-warnings-as-errors]
This great! ©.
How can I make the compiler, clang or GCC detect the same issue too? Either by activating some warning option or by some (non-standard?) attribute?
I tried using -Wmove in clang and the [[consumed]] attribute but they didn't help.
Perhaps I used them incorrectly.
The code is here: https://godbolt.org/z/18hr4vn7x (the lower panel is clang-tidy and the mid panel on the right is the [empty] compiler output)
Is there a chance a compiler will warn about this or it is just too costly for the compiler to check for this pattern?
I found one way to do it, using attributes in clang. .
(A GCC or a more standard solution is still welcome.)
needs clang 6 or higher
mark the class as "consumable"
mark the method(s) "callable-when-unconsumed" (not sure how to make this the default)
class [[clang::consumable(unconsumed)]] a_class {
std::unique_ptr<int> p_;
public:
[[clang::callable_when(unconsumed)]]
void f() {}
// private: [[clang::set_typestate(consumed)]] void invalidate() {} // not needed but good to know
};
https://godbolt.org/z/45q8vzdnc
The recipe is simplified from https://awesomekling.github.io/Catching-use-after-move-bugs-with-Clang-consumed-annotations/ .
I couldn't find detailed documentation on how to use the these features.
It is simplified because:
a) it seems that "clang-consumable" moved object becomes "consumed" by default when moved-from, so it is not necessary to write a special function to invalidate if not necessary (no need for [[clang::set_typestate(consumed)]]).
b) constructors seem to leave the object in an unconsumed state by default (no need for [[clang::return_typestate(unconsumed)]]);
If a compiler isn't built with a setting to do this, then you can't make it do this. Use-after-move is a legitimate thing in C++, so no compiler is obligated to consider it an error.
These kinds of things are what static analyzers are for.

Unique pointer in base class prohibts instantiation with error "attempting to reference a deleted function"

I updated my C++ toolchain from Visual Studio 2013 to Visual Studio 2017/2019.
Now I am experiencing a number of compile errors in the form:
<source>(13): error C2280: 'OfflineFixture::OfflineFixture(const OfflineFixture &)': attempting to reference a deleted function
<source>(8): note: compiler has generated 'OfflineFixture::OfflineFixture' here
<source>(8): note: 'OfflineFixture::OfflineFixture(const OfflineFixture &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)'
A unique pointer is a member of class that has no constructor and destructor.
In this case Visual Studio allows to instantiate an object this way:
OfflineFixture a{}; // works!
But using:
auto&& a = OfflineFixture{};
gives above compile error.
const auto& a = OfflineFixture{};
also gives above compile error.
Please have a look here:
https://gcc.godbolt.org/z/XtP40t
My question is: Is my code wrong?
The given examples compiles using:
gcc (9.1 and lower)
clang
Visual Studio 2013
But it fails in:
Visual Studio 2015
Visual Studio 2017
Visual Studio 2019
One way to fix this is to implement a default constructor in OfflineFixture.
Minimal example:
#include <memory>
struct OfflineFixture
{
void x() const {}
int i;
std::unique_ptr<int> m_calc;
};
int test() {
#if 1
const auto&& a = OfflineFixture{};
#else
OfflineFixture a{};
#endif
a.x();
return 0;
}
Let's make some preliminary points here.
In general, the statement:
const auto&& a = Foo{};
is perfectly legal in C++. Moreover, it is not true that it is undefined behavior. In fact, this is a perfect example of Reference Initialization with lifetime extension of a temporary.
Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference [...].
It continues with some exceptions (I won't quote all of them), but the declaration of a new object is not part of those exceptions.
Regarding your class, it appears to be clear the problem is the std::unique_ptr member.
Here a minimal example:
#include <memory>
struct OfflineFixture {
std::unique_ptr<int> m_calc;
};
void test() {
const auto&& a = OfflineFixture{}; // <--- Error in msvc
}
Somehow, MSVC tries to create a copy of the object. Indeed, the error is about invoking the copy constructor (which is deleted because of std::unique_ptr).
Since no copies should be performed here, it appears to be a msvc bug.
Note: gcc and clang compile fine with that.
I have added constructor and move constructor as shown in below code.
This resolves the issue.
struct OfflineFixture
{
void x() const {}
int i;
std::unique_ptr<int> m_calc;
OfflineFixture(){}
OfflineFixture(OfflineFixture &&){}
//implicit
OfflineFixture(const OfflineFixture&) = default;
OfflineFixture& operator=(const OfflineFixture&) = default;
~OfflineFixture() = default;
};
As, const auto&& a = OfflineFixture{}; requires constructor and move constructor
I hope it helps!
It is interesting to note that MSVC shows this erroneous behavior only if you do not provide a constructor or if you provide a default one:
struct OfflineFixture {
OfflineFixture()=default;
//...
};
The example can be fixed by providing a constructor with an empty block:
struct OfflineFixture {
OfflineFixture(){}
//...
};
which compiles just fine.

Why does MSVC 12.0 ignore private constructors if the copy assignment operator is deleted?

Consider the following code:
class Test
{
private:
Test() = default;
};
Test t;
int main() { }
Both MSVC 12 and 14 (Visual Studio 2013 and 2015, respectively) refuse to compile this, as expected - the constructor is private and so we cannot create objects of that type outside of the class itself.
However, let's now make one small change:
class Test
{
private:
Test() = default;
Test operator=(const Test& rhs) = delete;
};
Test t;
int main() { }
To my surprise, it will compile fine in MSVC 12 (14 still gives the same error). Why is it so? My first thought was that maybe this was standard behavior in an older version of C++, but then I realized that the very concept of deleting constructors was only introduced in C++11.
Is this a bug?
Well, MSVC 12 does have partial c++ 11 support, but I do believe that compiling is a bug. I encountered something similar using MSVC 12 where I could access private fields from non-static template functions, when I clearly shouldn't have. It was a coding oversight that strangely compiled. I thought it was weird so I compiled it with GCC and sure enough GCC said "No Good!". I think it was fixed in the November CTP though, since I can't seem replicate it.

Accepting rvalue as lvalue reference [duplicate]

string foo() { return "hello"; }
int main()
{
//below should be illegal for binding a non-const (lvalue) reference to a rvalue
string& tem = foo();
//below should be the correct one as only const reference can be bind to rvalue(most important const)
const string& constTem = foo();
}
GCC is the good one to give a compile error: invalid initialization of non-const reference of type std::string& from a temporary of type std::string
VS2008 is not too bad as at least it gives a compile warning:
warning C4239: nonstandard extension used : 'initializing' :
conversion from std::string to std::string & A non-const
reference may only be bound to an lvalue
Here comes the problematic one - VS2010(SP1) comples fine WITHOUT any
error or warning, WHY ??!!
I know rvalue reference in VS2010 can be used to bind with rvalue but I am NOT using &&, instead in the demo code, I was just using non-const lvalue reference !
Can somone help me explain the behavior of VS2010 here? Is it a bug !?
Thanks
That is a known issue/feature of the VS compilers. They have always allowed that and there does not seem to be any push into removing that extension.
The compiler will issue an error with Disable Language Extensions turned on, and a warning at /W4. However, removing this code will break previously compiling code, and Microsoft is very reluctant to do that. This is also why they won't fix their SFINAE support.
Several years and many versions of Visual Studio later, we still have this "extension" causing surprises and headaches. Sigh...
The fix is to simply turn warning C4239 into an error. This will prevent MSVC from compiling code that attempts to bind a non-const lvalue reference to a temporary, and give you a nice clear compiler error. Simply add /we4239 to your compiler definitions or cl command line arguments.
In Visual Studio:
Project Properties > C/C++ > All Options > Treat Specific Warnings As Errors > add 4239, and make sure to separate any other numbers with a semicolon.
In CMake:
if(MSVC)
add_definitions("/we4239")
endif()
This seems to work far better than disabling all language extensions with /Za, which officially not recommend. On my large code base, adding /Za caused over 1500 compiler errors from Microsofts's own winnt.h header.
There is a much nastier variant of this problem:
class Foo {
int _val;
public:
Foo(int v) : _val(v) {}
void F() { std::cout << _val << std::endl; }
};
class Bar {
Foo& f;
public:
Bar(Foo& f) : f(f) {}
void F() { f.F(); }
};
int main() {
Bar b(Foo(3));
b.F();
}
So: to what does b.f point during the call to b.F()? The above example, compiled with VS2013 default Debug settings, runs without crashing and prints 3, but I'd suspect that any much more complex example will lead to stack corruption. If it doesn't and the compiler is doing something 'clever' to make it work, then I guess what it is really doing is this:
class Foo {
int _val;
public:
Foo(int v) : _val(v) {}
void F() { std::cout << _val << std::endl; }
};
class Bar {
Foo f;
public:
Bar(Foo&& f) : f(f) {}
void F() { f.F(); }
};
int main() {
Bar b(Foo(3));
b.F();
}

One VS2010 bug ? Allowing binding non-const reference to rvalue WITHOUT EVEN a warning?

string foo() { return "hello"; }
int main()
{
//below should be illegal for binding a non-const (lvalue) reference to a rvalue
string& tem = foo();
//below should be the correct one as only const reference can be bind to rvalue(most important const)
const string& constTem = foo();
}
GCC is the good one to give a compile error: invalid initialization of non-const reference of type std::string& from a temporary of type std::string
VS2008 is not too bad as at least it gives a compile warning:
warning C4239: nonstandard extension used : 'initializing' :
conversion from std::string to std::string & A non-const
reference may only be bound to an lvalue
Here comes the problematic one - VS2010(SP1) comples fine WITHOUT any
error or warning, WHY ??!!
I know rvalue reference in VS2010 can be used to bind with rvalue but I am NOT using &&, instead in the demo code, I was just using non-const lvalue reference !
Can somone help me explain the behavior of VS2010 here? Is it a bug !?
Thanks
That is a known issue/feature of the VS compilers. They have always allowed that and there does not seem to be any push into removing that extension.
The compiler will issue an error with Disable Language Extensions turned on, and a warning at /W4. However, removing this code will break previously compiling code, and Microsoft is very reluctant to do that. This is also why they won't fix their SFINAE support.
Several years and many versions of Visual Studio later, we still have this "extension" causing surprises and headaches. Sigh...
The fix is to simply turn warning C4239 into an error. This will prevent MSVC from compiling code that attempts to bind a non-const lvalue reference to a temporary, and give you a nice clear compiler error. Simply add /we4239 to your compiler definitions or cl command line arguments.
In Visual Studio:
Project Properties > C/C++ > All Options > Treat Specific Warnings As Errors > add 4239, and make sure to separate any other numbers with a semicolon.
In CMake:
if(MSVC)
add_definitions("/we4239")
endif()
This seems to work far better than disabling all language extensions with /Za, which officially not recommend. On my large code base, adding /Za caused over 1500 compiler errors from Microsofts's own winnt.h header.
There is a much nastier variant of this problem:
class Foo {
int _val;
public:
Foo(int v) : _val(v) {}
void F() { std::cout << _val << std::endl; }
};
class Bar {
Foo& f;
public:
Bar(Foo& f) : f(f) {}
void F() { f.F(); }
};
int main() {
Bar b(Foo(3));
b.F();
}
So: to what does b.f point during the call to b.F()? The above example, compiled with VS2013 default Debug settings, runs without crashing and prints 3, but I'd suspect that any much more complex example will lead to stack corruption. If it doesn't and the compiler is doing something 'clever' to make it work, then I guess what it is really doing is this:
class Foo {
int _val;
public:
Foo(int v) : _val(v) {}
void F() { std::cout << _val << std::endl; }
};
class Bar {
Foo f;
public:
Bar(Foo&& f) : f(f) {}
void F() { f.F(); }
};
int main() {
Bar b(Foo(3));
b.F();
}