I would like to use libtooling to test whether the defined by a CXXRecordDecl is copy constructable.
I have already tried :
hasCopyConstructorWithConstParam()
hasTrivialCopyConstructor() || hasNonTrivialCopyConstructor()
Unfortunately, both of those expressions return true if the class's copy constructor is implicitly deleted. This can occur if the class inherits from a non-copyable class or has a member variable that is non-copyable.
The logic to test whether a class is copy constructable is non-trivial and must be exist somewhere in clang. How can I test if a class is copy constructable with libtooling?
Turn comment into answer:
You can retrieve the constructor with CXXRecordDecl::ctor_begin
and check CXXConstructorDecl::isDeleted().
Related
I am referring to this excellent blog post regarding the "Unforgettable Factory Registration Pattern": http://www.nirfriedman.com/2018/04/29/unforgettable-factory/
I've noticed that this stops working when the self-registering types are default constructible. In the example provided by the code, the self-registering types (Animal base class) defines a constructor with an int parameter. If I slightly modify the code to make Animal become default constructible, the data map in the factory remains empty and objects of type Cat and Dog can no longer be constructed through the factory.
As far as I understand the issue lies in the fact that the Animal class no longer calls the registerT function. However, I fail to understand why that is and what modification(s) are necessary to make it work with default constructible classes.
Can somebody shed some light on this?
Useful:
Modified example: http://coliru.stacked-crooked.com/a/55fe8edc094c88a8
Original example: http://coliru.stacked-crooked.com/a/11473a649e402831
The int argument is a red herring. When changing the argument list you modified
Dog(int x) : m_x(x) {}
to be
Dog() = default;
From a user-provided constructor, to a defaulted one.
In effect, this means the compiler (I checked Clang and GCC) doesn't emit a function definition for Dog::Dog(), so it doesn't odr-use Animal::Registrar<Dog>::Registrar<Dog>(), which means its definition isn't instantiated (member function bodies only are only instantiated when odr-used). And so we never odr-use its registered static data member.
If the constructor is defaulted, then we never actually end up registering the factory function into the map. And indeed, after it becomes user-provided again, things start to work.
I am however unable to understand why that makes a difference. The user-provided constructor is still an inline member function. And inline function definitions are tied to odr-usage too. But perhaps that should be the subject of another question.
What are the rules wether to define a default constructor for myClass or not?
Example: the user defined type Book(which could have title, author, ISBN and genre as its datamembers) should probably have no default constructor, since there is no "default book".
Is there some guide which handles this topic?
If you reasonably can, make your type regular. That means you should support:
default construction
copy construction
assignment
equality comparision
That only requires defining == and ensuring that default construction is still available.
Consider the following code:
#include <memory>
#include <vector>
class A
{
private:
std::vector<std::unique_ptr<int>> _vals;
};
int main()
{
A a;
//A a2(a);
return 0;
}
Compiler A compiles this without issue unless I uncomment out the line A a2(a); at which point it complains about the copy constructor for std::unique_ptr being deleted, and therefore I can't copy construct A. Compiler B, however, makes that complaint even if I leave that line commented out. That is, compiler A only generates an implicitly defined copy constructor when I actually try to use it, whereas compiler B does so unconditionally. Which one is correct? Note that if I were to have used std::unique_ptr<int> _vals; instead of std::vector<std::unique_ptr<int>> _vals; both compilers correctly implicitly delete both copy constructor and assignment operator (std::unique_ptr has a explicitly deleted copy constructor, while std::vector does not).
(Note: Getting the code to compile in compiler B is easy enough - just explicitly delete the copy constructor and assignment operator, and it works correctly. That isn't the point of the question; it is to understand the correct behavior.)
From [class.copy.ctor]/12:
A copy/move constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used ([basic.def.odr]), when it is needed for constant evaluation ([expr.const]), or when it is explicitly defaulted after its first declaration.
A's copy constructor is defaulted, so it's implicitly defined only when it is odr-used. A a2(a); is just such an odr-use - so it's that statement that would trigger its definition, that would make the program ill-formed. Until the copy constructor is odr-used, it should not be defined.
Compiler B is wrong to reject the program.
Note: My answer is based on your comment:
[...] it's only on Windows, and only when I explicitly list class A as a DLL export (via, e.g., class __declspec(dllexport) A) that this happens. [...]
On MSDN we can learn that declaring a class dllexport makes all members exported and required a definition for all of them. I suspect the compiler generates the definitions for all non-deleted functions in order to comply with this rule.
As you can read here, std::is_copy_constructible<std::vector<std::unique_ptr<int>>>::value is actually true and I would expect the supposed mechanism (that defines the copy constructor in your case for export purposes) checks the value of this trait (or uses a similar mechanism) instead of actually checking whether it would compile. That would explain why the bahviour is correct when you use unique_ptr<T> instead of vector<unique_ptr<T>>.
The issue is thus, that std::vector actually defines the copy constructor even when it wouldn't compile.
Imho, a is_copy_constructible check is sufficient because at the point where your dllexport happens you cannot know whether the implicit function will be odr-used at the place where you use dllimport (possibly even another project). Thus, I wouldn't think of it as a bug in compiler B.
While I can't confirm this behavior (I do not have access to Windows compiler, and OP claims the bug happens with icc on Windows platform), taking the question at it's face value, the answer is - compiler B has a gross bug.
In particular, implicitly-declared copy constructor is defined as deleted, when ...
T has non-static data members that cannot be copied (have deleted,
inaccessible, or ambiguous copy constructors);
https://en.cppreference.com/w/cpp/language/copy_constructor
Thus, conforming compiler must semantically generate deleted copy-constructor, and successfully compile the program since such constructor never called.
When declaring a class/struct/union the compiler will generate the default methods (rule of three). This also will happen when = default'ing these methods.
How do the default methods exactly look like?
For each of these methods, the compiler defines default inline methods that call the default methods of each attribute of the object. (so a pointer won't be initialized neither any built-in type).
For example let consider the default constructor. According to the C++ Standard
An implicitly-declared default constructor is an inline public member
of its class.
and
The implicitly-defined default constructor performs the set of
initializations of the class that would be performed by a user-written
default constructor for that class with no ctor-initializer (12.6.2)
and an empty compound-statement.
So it looks like
struct A
{
A() {}
};
except that it is not explicitly declared and defined.
About copy constructor you can read at my personal forum
http://cpp.forum24.ru/?1-1-0-00000021-000-0-0-1388485669
Though it is written in Russian but you can translate it for example using google service translate.
Those method will do the minimum necessary to initialize the class.
Default construtor - do nothing with simple members but call the constructors of more complex members (class/struct) as well as call the ctor of its super class.
Copy constructor will perform a shallow copy (memcpy).
This might be a silly question, but...
I've been writing a number of classes that utilize non-copyable members. These classes are never initialized via the copy constructor in my source. When I try to compile without supplying my own copy-constructor, g++ throws out many errors about how it can't build a default copy constructor, due to the non-copyable member objects.
Is there a way to tell the compiler to just not give me a copy constructor?
EDIT: Yeah... feels silly... I had a case where I was invoking the copy-constructor by accident in a boost::bind call. Lesson learned.
The usual way to make things noncopyable is to declare but not define a copy constructor, and make it private so nothing can call it.
The next revision of the language will provide an explicit way to suppress these generated functions.
If you don't actually cause the copy-constructor to be called then it is not an error if the compiler would be unable to generate one. It sounds like you are (possibly indirectly) causing the copy-constructor to be used.
You can suppress the compiler generated one by declaring your own copy-constructor (you don't need to define it if you're not using it). You can place it in the private section of your class.
If this changes the error to say that the copy-constructor is inaccessible or you get link errors then you really are causing the copy-construtor to be used and you need to analyze why this is.
Not in the current version of C++. In C++ 0x, there will be an =delete; syntax to tell it that you don't want one of the special member functions the compiler will generate by default if you don't defined one yourself.
Until the new C++ 0x standard is fully supported, the best you can do is to delclare a version of the special member function, but not implement them. Normally they are made private (to help make it clear that they shouldn't be used).
Class foo
{
// ... rest of definition
private:
foo (const foo& rhs); // Do Not Implement
const foo& operator= (const foo& rhs); // Do Not Implement
};
No :)
If you want your class to be non-copyable use something like boost::noncopyable
class MyClass : private boost::noncopyable
{
}
or use a parametrizied macro in your class definition that declares a private copy constructor.