Undefined reference to a destructor in clang - c++

Compiling following code gives "undefined reference to `A::~A()":
#include <cstdlib>
#include <memory>
template <typename T>
struct A {
A() {}
~A() {}
};
struct Aggregate {
using key_vector = A<char>;
using value_vector = A<int>;
value_vector vals;
key_vector keys;
};
int
main()
{
auto x = malloc(sizeof(Aggregate));
new (x) Aggregate{};
return 0;
}
the problem is present on clang 7.0 and 6.0 (possibly also some oolder versions). See: https://godbolt.org/z/GNPk3V
On newer clang versions and on gcc it works fine.
Is this expected or is it some kind of bug in clang?

This appears to be Bug 28280, fixed by https://reviews.llvm.org/D45898:
If an initializer in a braced-init-list is a C++ class that has a non-trivial destructor, mark the destructor as referenced. This fixes a crash in CodeGenFunction::destroyCXXObject that occurs when it tries to emit a call to the destructor on the stack unwinding path but the CXXRecordDecl of the class doesn't have a CXXDestructorDecl for the destructor.
This example does use a braced-init-list and emits the destructor call right before calling _Unwind_Resume. The destructor is not trivial. Changing the initialization to use () instead of {} makes the error go away because it's no longer initialized with a braced-init-list. The destructor call in my comment would presumably cause the destructor to be marked as referenced. Perhaps enabling optimization plays with the same things that make this appear only for non-trivial destructors.

Related

Class seems to be movable with user defined destructor

I'm trying to figure out why this code does not compile?
I've created user defined destructor, so the move ctor and move assignment operator should not be created. So why complication fails, saying that my class does not fulfill the requirements?
#include <iostream>
#include <concepts>
#include <type_traits>
template<class T> requires (!std::movable<T>)
struct Singleton
{
};
struct Widget
{
~Widget() = default;
};
int main()
{
auto x = Singleton<Widget>{};
}
Edit. The same goes for this version.
#include <iostream>
#include <concepts>
#include <type_traits>
extern void fun();
template<class T> requires (!std::movable<T>)
struct Singleton
{
};
struct Widget
{
~Widget()
{
fun();
}
};
int main()
{
auto x = Singleton<Widget>{};
}
Error message
:23:28: note: constraints not satisfied
: In substitution of 'template requires !(movable) > struct Singleton [with T = Widget]':
:23:28: required from here
:8:8: required by the constraints of 'template requires !(movable) struct Singleton'
:7:30: note: the expression '!(movable) [with T = Widget]' evaluated to 'false'
7 | template requires (!std::movable)
Compiled used gcc (trunk) on godbolt. The only compilation flag I've used is -std=c++20
https://godbolt.org/z/sb535b1qv
There is no move constructor in either example. But that's not what std::movable checks. It defers the check you are intesteded in to std::move_constructible. That concept merely checks that an object can be both direct-initialized and copy-initialized from an rvalue.
And a copy constructor was able to initialize from an rvalue since the days of C++98. And it still works with the addition of move operations. That's why the trait is satisfied. Widget a; Widget b = std::move(a); is still valid, even in the absence of a move constructor, because the compiler provided copy constructor makes this possible.
The compiler not generating a move constructor was a design decision made to keep old code working when it made the move to C++11. Such code would have adhered to the rule of three, and so swapping the old copy for a compiler generated move was deemed risky. But old code could still initialize objects from rvalues, so the copy constructor retained its old function.
It is noteworthy that the trait may work as one expects in the future. A defaulted copy constructor being defined here is a deprecated feature:
[class.copy.ctor]
6 If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted ([dcl.fct.def]). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor ([depr.impldec]).

is the undefined reference to vtable error solved by modern g++ compilers?

according to this virtual functions must be defined otherwise linker complains & reports error "undefined reference to vtable", but why doesn't ideone compiler give any errors for the following code?
#include <iostream>
using namespace std;
class Test
{
public:
Test()
{
cout<<"test() is called\n";
}
virtual void test();
};
int main() {
Test t;
// your code goes here
return 0;
}
You didn't read the documentation properly. The first sentence in the relevant paragraph says:
The ISO C++ Standard specifies that all virtual methods of a class that are not pure-virtual must be defined, but does not require any diagnostic for violations of this rule [class.virtual]/8.
So, it is expected that you may not get an error, especially since you are not actually invoking test() (despite the lie in the constructor's output).
Speaking practically, you are likely to get this diagnostic only under the following circumstances:
you call a virtual function that you did not define
you instantiate an object whose virtual destructor you did not define
But make no mistake: your program has undefined behaviour regardless.

C++11 private default constructor

The following C++11 code compiles successfully on my GCC 4.8:
struct NonStack
{
private:
NonStack() = default;
public:
static NonStack* Create(){
return new NonStack;
}
};
NonStack a;
int main() { }
However the following gives a compilation error:
struct NonStack
{
private:
NonStack(){}
};
NonStack a;
int main() { }
Why does the first one succeed? Shouldn't the private defaulted constructor prohibit creation of an object via NonStack a;?
This is gcc bug 54812, the compiler fails to respect access specifiers for explicitly defaulted special member functions. Bug 56429, which is marked as a duplicate of the earlier one, has a test case that is almost identical to the example in the question.
The solutions are to upgrade to gcc4.9, which resolves the issue. Or create an empty body for the constructor, instead of explicitly defaulting it, as you've done in the second example.

Default constructor won't compile inside template class when brackets are included (g++4.6.1)

I couldn't find any information on Google about this, In the following example:
#include <iostream>
class Default
{
public:
void Print()
{
std::cout << "This is a message\n";
}
};
template <class C = Default>
class Template
{
public:
static void Test()
{
Default oDefault();
}
};
int main()
{
return 0;
}
the code fails to compile with the error:
In static member function ‘static void Template::Test()’:
19:22: error: default template arguments may not be used in function templates without -std=c++0x or -std=gnu++0x
The trouble is that it doesn't like the brackets appearing on that line and I don't understand why. If I remove the brackets the code compiles just fine. Also if I remove the template declaration (line 13) it also compiles just fine. Is this a bug or is there some rule somewhere about exactly this situation?
I'm using g++4.6.1 (gcc version 4.6.1 (Ubuntu/Linaro 4.6.1-9ubuntu3))
Change it to:
Default oDefault = Default(); // Version 1
Though you can use:
Default oDefault; // Version 2
This has a slightly different meaning.
Version 1: Causes the POD members to be zero initialized (in some situations).
Version 2: Causes the POD members to be un-initialized (in some situations).
Through version one looks like it invokes an extra copy construction this is not the case (in most compilers) as the extra copy will be elided by the compiler and a simple normal (zero-initialized) construction will happen.
You should prefer zero-initialization to default (in general) as if the class (or any members type) does not have a user defined constructor (Like Default) then the difference is that default-initialization leaves POD members undefined while zero-initialized leaves POD members initialized to zero. Now you may think now that my class has no members so it does not matter. But what happens if you modify the class are you going to go and find all instances and update them; Best to use version one and let the compiler do the correct thing.
For all the mind blowing details see:
https://stackoverflow.com/a/620402/14065
https://stackoverflow.com/a/1613383/14065
The reason your initial version did not work is that it is actually a forward declaration of a function. This is caused by the complex syntax of C++ and just one of the rules you need to remember. You can look this up as "The Most Vexing Parse".
Default oDefault();
The compiler sees this as an function declaration and not as creating an object.
It declares a function oDefault() which takes no parameters and returns a Default object.
Change it to:
Default oDefault;

Purpose of boost::checked_delete

I don't understand the purpose of boost::checked_delete. The documentation says:
The C++ Standard allows, in 5.3.5/5,
pointers to incomplete class types to
be deleted with a delete-expression.
When the class has a non-trivial
destructor, or a class-specific
operator delete, the behavior is
undefined. Some compilers issue a
warning when an incomplete type is
deleted, but unfortunately, not all
do, and programmers sometimes ignore
or disable warnings.
The supplied function and class
templates can be used to prevent these
problems, as they require a complete
type, and cause a compilation error
otherwise.
So the C++ standard allows you to delete incomplete types, which causes undefined behavior if the type has a non-trivial destructor. What? How can an incomplete type have any destructor at all? Isn't an incomplete type just a prototype?
The most common example of an incomplete type is one that has only been declared:
// this file does not include the definition of foo
class foo;
void bad(foo *f)
{
delete f; // undefined behavior if there exists foo::~foo
}
In reality, the definition of foo may look like this:
class foo
{
public:
~foo() { ... };
};
But if the top code has not 'seen' the class definition and just sees the class declaration, the code will compile.
Consider the following:
Foo.h:
#ifndef Foo_H
#define Foo_H
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
class Foo : private boost::noncopyable
{
public:
Foo();
~Foo();
void do_something_interesting();
private:
class Impl; // incomplete type
boost::scoped_ptr<Impl> impl;
};
#endif
Foo.cpp:
#include "Foo.h"
#include <string>
#include <iostream>
class Foo::Impl
{
public:
Impl() : name("Foo::Impl")
{}
void say_hi()
{ std::cout << name << " says hi!" << std::endl; }
std::string name;
};
Foo::Foo()
: impl(new Impl)
{}
Foo::~Foo()
{}
void Foo::do_something_interesting()
{ impl->say_hi(); }
Given this (contrived) example, you cannot inline either Foo::Foo or Foo::~Foo because the type is incomplete. By defining both in a context where the type Foo::Impl is a complete type, you can safely delete the type. boost::checked_delete does this safety check for you, and it's purely a compile-time cost. If you either inline Foo::~Foo or omit it entirely, you will get an error from boost::checked_delete wherever you try to destroy a Foo instance.
C++ allows you to use delete on variables that at the time are pointers to incomplete types.
struct S; // incomplete
int main() {
S* s = NULL;
delete s; // legal
}
The compiler doesn't know at that point what S really is. If it turns out S has a non-trivial destructor, then the compiler is not required to detect that problem.
Practically speaking, what probably happens is that when the compiler encounters the delete instruction on an incomplete type, it fills in a call to what it expects will be the type's ordinary compiler-generate default destructor. And if that's what the destructor turns out to be, then everything's fine. But if it turns out that S has a non-trivial destructor, or if it provides its own special method of deletion, then what the compiler filled in earlier will be wrong. The compiler, however, allowed to assume that it correctly compiled the delete instruction and never look back. When that assumption is wrong, you'll get undefined behavior.
The Boost function ensures that it's called only on complete types, thus avoiding the undefined behavior that may occur on incomplete types.