Variadic template constructor and copy constructor - c++

I don't understand why the compiler chooses the copy constructor of my Production class and has no other candidate functions.
I made a minimal example to demonstrate the error:
#include <string>
#include <typeindex>
#include <iostream>
struct DummyProduction {
};
struct Dep {
};
struct Pro {
};
class ModuleBase {
};
template<typename Production = DummyProduction>
class Provider {
public:
template<typename... Dependencies>
Provider(ModuleBase& module, Dependencies... args)
{
std::cout << "Provider called!" << std::endl;
}
Provider(const Provider&) = delete;
};
class TargetController : public ModuleBase,
public Provider<Pro>,
public Provider<>
{
public:
TargetController();
private:
Dep p;
};
TargetController::TargetController() :
ModuleBase(),
Provider<Pro>(*this, &p),
Provider<>(*this),
p()
{
}
int main()
{
TargetController x;
return 0;
}
I tried it with gcc and clang. Here is a link to the non working example: link.
For the Provider<Pro>(*this, p) the right constructor is called. But for the second example Provider<>(*this) the compiler tries to call the copy-constructor.
From what I understood from the Overload resolution page all functions that match the expressions are should get inside the candidate function set. But either the variadic constuctor is not inside the set for the Provider without dependencies or the compiler chooses the copy-constructor in spite of beeing deleted.
Is there a way to avoid this behaviour?

The fact that an function/method is deleted doesn't remove it from overload list.
And the copy constructor has higher priority over the template method (as it is not an exact match).
As workaround you may cast this to the expected type:
TargetController::TargetController() :
ModuleBase(),
Provider<Pro>(*this, p),
Provider<>(static_cast<ModuleBase&>(*this))
{
}
Demo

Templated constructors are never copy constructors. When you call base's constructor and pass it a reference to base (or a derived) the copy constructor is supposed to be called. The template isn't an option in that context.
Provider<>(*this)
Is such a context.
Worth noting that I believe VS still gets this wrong. In that compiler you have to cast to base& or it'll call the template.

Related

Storing a class that uses the PIMPL idiom in a std::vector

I am writing an application that needs to store objects of a class that uses the PIMPL idiom in a std::vector. Because the class uses std::unique_ptr to store a pointer to it's implementation and std::unique_ptr is not copyable, the class itself is not copyable. std::vector should still work in this case because the class is still movable.
To avoid creating a copy I tried using emplace_back to construct the elements directly into the vector, but for some reason it still complains that it is trying to call the copy constructor!
I've written a simple example to demonstrate the problem.
test.h:
#pragma once
#include <memory>
// Simple test class implemented using the PIMPL (pointer to implementation) idiom
class Test
{
public:
Test(const int value);
~Test();
void DoSomething();
private:
// Forward declare implementation struct
struct Impl;
// Pointer to the implementation
std::unique_ptr<Impl> m_impl;
};
test.cpp
#include "test.h"
#include <iostream>
// Implementation struct definition
struct Test::Impl
{
Impl(const int value)
: m_value(value)
{}
void DoSomething()
{
std::cout << "value = " << m_value << std::endl;
}
private:
int m_value;
};
// Construct the class and create an instance of the implementation struct
Test::Test(const int value)
: m_impl(std::make_unique<Impl>(value))
{}
Test::~Test() = default;
// Forward function calls to the implementation struct
void Test::DoSomething()
{
m_impl->DoSomething();
}
main.cpp:
#include "test.h"
#include <vector>
int main()
{
std::vector<Test> v;
// Even though I'm using "emplace_back" it still seems to be invoking the copy constructor!
v.emplace_back(42);
return 0;
}
When I try to compile this code I get the following error:
error C2280: 'Test::Test(const Test &)': attempting to reference a deleted function
This leads to two questions...
Why is it attempting to use the copy constructor even though I explicitly used emplace_back?
How can I get this to compile without errors? The object is movable so according to the standard it should be able to be stored in a std::vector.
Add Test(Test&&) noexcept; and Test& operator=(Test&&) noexcept; then =default them in your cpp file.
You should probably make Test(const int value) explicit while you are at it.
In modern gcc/clang at least, you can =default the move ctor in the header. You cannot do this to operator= because it can delete the left hand side pointer, nor the zero argument constructor (something to do with constructing the default deleter?) or destructor (which has to call the default deleter).

How is it possible to initialize a struct, if it has non-copyable members?

I would like to create a struct, which has some primitive members and a unique_ptr, which can not be copied. I tried to solve it by disabling copy and adding the default move constructor, but it does not work. The compiler tries to find a 2 argument constructor, but there isn't any:
In function 'int main()':
37:3: error: no matching function for call to 'ListElem::ListElem(<brace-enclosed initializer list>)'
37:3: note: candidates are:
25:2: note: ListElem::ListElem(ListElem&&)
25:2: note: candidate expects 1 argument, 2 provided
20:5: note: constexpr ListElem::ListElem()
20:5: note: candidate expects 0 arguments, 2 provided
The code is:
// Example program
#include <iostream>
#include <memory>
#include <vector>
class MyObj {
public:
virtual ~MyObj(){}
};
class MyObj2 : public MyObj{
public:
virtual ~MyObj2(){}
};
struct ListElem {
std::unique_ptr<MyObj> item;
int some_counter = 0;
ListElem() = default;
ListElem(ListElem const& e) = delete;
ListElem& operator=(ListElem const& e) = delete;
ListElem(ListElem&& e) = default;
ListElem& operator=(ListElem&& e) = default;
};
int main()
{
std::vector<ListElem> elems;
ListElem item{
std::unique_ptr<MyObj>(new MyObj2()),
10
};
elems.push_back(std::move(item));
}
How could I solve this error?
Edit
I changed the constructor to this:
ListElem(std::unique_ptr<MyObj>&& _item, int _some_counter): item(_item), some_counter(_some_counter) {}
But now I get error: use of deleted function
You can remove all constructor declarations from your class(1). The class will then be an aggregate and will be initialisable with aggregate initialisation (what you're trying to do). Note that there's no need to manually delete the copy constructor or default the move constructor, both will happen automatically.
[Live example]
Of couse, the alternative is to provide a two-parameter constructor taking the arguments you want to pass in. Note that even in this case, you don't need to manually delete & default the copy/move operations.
(1) Keep in mind that struct and class both introduce class types. The only difference between them is the implicit access control for bases and members (public and private, respectively); they are otherwise identical.
Initializing a struct directly without constructor is called aggregate initialization. This works only in some cases. For eg. the class should not have any constructor on its own (defaulted ones are fine). It looks like some restrictions were lifted in c++14, so as posted in the other answer, it works fine in c++14 or later. But not in c++11. https://gcc.godbolt.org/z/ZTFUz3
ListElem item{
std::unique_ptr<MyObj>(new MyObj2()),
10
};
For your edit, the reason for failure is you missed to move the unique_ptr. Add std::move and it should work fine in both versions.
https://gcc.godbolt.org/z/uFmZvC
ListElem(std::unique_ptr<MyObj>&& _item, int _some_counter)
: item(std::move(_item)),
some_counter(_some_counter)
{}
The problem is the initialization of the member some_counter: this does not allow the aggregate initialization of the struct, check here https://en.cppreference.com/w/cpp/language/aggregate_initialization, it is explained that the class (struct) should not have no default member initializers (since C++11 until C++14).
Here the code changed:
// Example program
#include <iostream>
#include <memory>
#include <vector>
class MyObj {
public:
virtual ~MyObj(){}
};
class MyObj2 : public MyObj{
public:
virtual ~MyObj2(){}
};
struct ListElem {
std::unique_ptr<MyObj> item;
int some_counter;
ListElem() = default;
ListElem(ListElem const& e) = delete;
ListElem& operator=(ListElem const& e) = delete;
ListElem(ListElem&& e) = default;
ListElem& operator=(ListElem&& e) = default;
};
int main()
{
std::vector<ListElem> elems;
ListElem item { std::unique_ptr<MyObj>(new MyObj2()), 10};
elems.push_back(std::move(item));
}
Link to code: https://wandbox.org/permlink/InZQwGKFyWKmCJrL

How to get if a type is truly move constructible

Take for example this code:
#include <type_traits>
#include <iostream>
struct Foo
{
Foo() = default;
Foo(Foo&&) = delete;
Foo(const Foo&) noexcept
{
std::cout << "copy!" << std::endl;
};
};
struct Bar : Foo {};
static_assert(!std::is_move_constructible_v<Foo>, "Foo shouldn't be move constructible");
// This would error if uncommented
//static_assert(!std::is_move_constructible_v<Bar>, "Bar shouldn't be move constructible");
int main()
{
Bar bar {};
Bar barTwo { std::move(bar) };
// prints "copy!"
}
Because Bar is derived from Foo, it doesn't have a move constructor. It is still constructible by using the copy constructor. I learned why it chooses the copy constructor from another answer:
if y is of type S, then std::move(y), of type S&&, is reference compatible with type S&. Thus S x(std::move(y)) is perfectly valid and call the copy constructor S::S(const S&).
—Lærne, Understanding std::is_move_constructible
So I understand why a rvalue "downgrades" from moving to a lvalue copying, and thus why std::is_move_constructible returns true. However, is there a way to detect if a type is truly move constructible excluding the copy constructor?
There are claims that presence of move constructor can't be detected and on surface they seem to be correct -- the way && binds to const& makes it impossible to tell which constructors are present in class' interface.
Then it occurred to me -- move semantic in C++ isn't a separate semantic... It is an "alias" to a copy semantic, another "interface" that class implementer can "intercept" and provide alternative implementation. So the question "can we detect a presence of move ctor?" can be reformulated as "can we detect a presence of two copy interfaces?". Turns out we can achieve that by (ab)using overloading -- it fails to compile when there are two equally viable ways to construct an object and this fact can be detected with SFINAE.
30 lines of code are worth a thousand words:
#include <type_traits>
#include <utility>
#include <cstdio>
using namespace std;
struct S
{
~S();
//S(S const&){}
//S(S const&) = delete;
//S(S&&) {}
//S(S&&) = delete;
};
template<class P>
struct M
{
operator P const&();
operator P&&();
};
constexpr bool has_cctor = is_copy_constructible_v<S>;
constexpr bool has_mctor = is_move_constructible_v<S> && !is_constructible_v<S, M<S>>;
int main()
{
printf("has_cctor = %d\n", has_cctor);
printf("has_mctor = %d\n", has_mctor);
}
Notes:
you probably should be able to confuse this logic with additional const/volatile overloads, so some additional work may be required here
doubt this magic works well with private/protected constructors -- another area to look at
doesn't seem to work on MSVC (as is tradition)
How to find out whether or not a type has a move constructor?
Assuming that the base class comes from the upstream, and the derived class is part of your application, there is no further decision you can make, once you decided to derive 'your' Bar from 'their' Foo.
It is the responsibility of the base class Foo to define its own constructors. That is an implementation detail of the base class. The same is true for the derived class. Constructors are not inherited. Trivially, both classes have full control over their own implementation.
So, if you want to have a move constructor in the derived class, just add one:
struct Bar : Foo {
Bar(Bar&&) noexcept {
std::cout << "move!" << std::endl;
};
};
If you don't want any, delete it:
struct Bar : Foo {
Bar(Bar&&) = delete;
};
If you do the latter, you can also uncomment the second static_assert without getting an error.

use copy constructor when move constructor is declared but private

Say a given class A is defined with a public copy constructor and a private move constructor. If a function f returns an object of type A, and f is used to initialize a local instance of variable of type A, then by default (since the value returned is a rvalue) the compiler will try to use the move constructor. I believed it is sensible to expect the compiler to use the copy constructor once it detects that the move constructor is private, however to my surprise I received a compiler error stating that the move constructor is private. My question is as follows, given the following code:
#include<iostream>
using namespace std;
class A
{
friend A f();
public:
A(const A&) { cout << "copy\n"; }
private:
A() {}
A(A&&) { cout << "move\n"; }
};
A f()
{
A a;
return a;
}
int main()
{
A a = f();
}
How can I change the code (without changing A or f) so that I could initialize the variable in main using the copy constructor?
I would change the class since it's not sensible.
Alternatively derive from the class or wrap it.
If you just want a quick hack you can do
template< class Type >
Type& tempref( Type&& t ) { return t; }
then do
A a = tempref( f() )
Disclaimer: code not touched by compiler's hands.
The overload resolution is performed first, to select a function to call.
Access check is performed as a later step, checking if the selected function/constructor is possible to call.
This is done on purpose, so a private function is not called (because it is private). Having the compiler select another function to call in that case would not be productive.

Passing unnamed classes through functions

How do I pass this instance as a parameter into a function?
class
{
public:
void foo();
} bar;
Do I have to name the class?
It is copyable since I haven't made the class's copy ctor private.
So how is it possible if at all?
Maybe it would be better if you explicit what you want to do. Why do you want to create an unnamed class? Does it conform to an interface? Unnamed classes are quite limited, they cannot be used as parameters to functions, they cannot be used as template type-parameters...
Now if you are implmenting an interface then you can pass references to that interface:
class interface {
public:
virtual void f() const = 0;
};
void function( interface const& o )
{
o.f();
}
int main()
{
class : public interface {
public:
virtual void f() const {
std::cout << "bar" << std::endl;
}
} bar;
function( bar ); // will printout "bar"
}
NOTE: For all those answers that consider template arguments as an option, unnamed classes cannot be passed as template type arguments.
C++ Standard. 14.3.1, paragraph 2:
2 A local type, a type with no
linkage, an unnamed type or a type
compounded from any of these types
shall not be used as a
template-argument for a template
type-parameter.
If you test with comeau compiler (the link is for the online tryout) you will get the following error:
error: a template argument may not
reference an unnamed type
As a side note, comeau compiler is the most standard compliant compiler I know of, besides being the one with the most helpful error diagnostics I have tried.
NOTE: Comeau and gcc (g++ 4.0) give an error with the code above. Intel compiler (and from other peoples comments MSVS 2008) accept using unnamed classes as template parameters, against the standard.
Why should you create an anonymous class when you want to pass it to a function?
Just explicitly declare the class:
class Foo {
// ...
};
void Method(Foo instance);
int main() {
Foo bar;
Method(bar);
}
The 2nd possibility would be using a template-function, so the compiler would infer the type (Note that this is not standard-compatible!)
#include <iostream>
using namespace std;
template <typename T>
void SayFoo(T& arg) {
arg.Foo();
}
int main() {
class {
public:
void Foo() { cout << "Hi" << endl; }
} Bar;
Bar.Foo();
SayFoo(Bar);
return 0;
}
There is no problem with copying the class since the compiler generated the copy constructor automatically and you can use tools like boost::typeof in order to avoid referring to the type explicitly.
BOOST::AUTO(copy, Bar);
Another approch is using (relatively slow) runtime-polymorphism (interfaces/inheritance).
Yes you have to name the class to pass it a instance of to the function. As you have not provided your own copy ctor compiler will generate its own and use it.
Hmmm, nameless class.
You could pass a pointer to it as void *.
Since you cannot refer to it by name, you cannot call its constructor, copy constructor, or destructor except for the one instance, so you cannot copy it.
EDIT: you could still pass it to a template function which could copy it.
REEDIT: edit was wrong.