I am trying to understand at a non-superficial level why the following code does not compile:
#include <vector>
template<typename T>
struct wrapper {
T wrapped_value;
wrapper() {}
template<typename... Args>
wrapper(Args&&... args) : wrapped_value( std::forward<Args>(args)... ) {
}
};
struct A {
int a;
A(int i = 0) : a(i) {
}
};
int main() {
std::vector<wrapper<A>> v1;
std::vector<wrapper<A>> v2;
v1 = v2;
}
I can tell from the error message in the std::vector implementation that the above is failing because the perfect forwarding constructor of wrapper<T> matches the copy constructor. The copy constructor created by substitution into the constructor template would be
wrapper(wrapper<A>& w) : wrapped_value( w ) {
}
Because wrapped_value is of type A this is an error since A does not have a constructor accepting a wrapper<A>.
But isn't "substitution failure not an error"? So the constructor template fails when the compiler attempts to use it as a copy constructor -- why does this block the automatic generation of a copy constructor? Or does it not and the real problem has something to do with the implementation of std::vector?
Also, this is a toy example but what is the best way around this sort of thing in my real code when dealing with classes like this?
Use "pass-by-value-then-move" rather than perfect forwarding?
Just define the copy constructor as default?
Use an std::in_place_t parameter before the variadic parametes in the perfect forwarding constructor?
Disable the constructor template in the case of copy construction via enable_if et. al.
Substitution is not failing and special function generation is not being blocked. Template substitution leads to a constructor that is a better match than the compiler-generated copy constructor so it is selected which causes a syntax error.
Let's simplify the problem illustrated in the question by getting rid of usage of std::vector. The following will also fail to compile:
#include <utility>
template<typename T>
struct wrapper {
T wrapped_value;
wrapper() {}
template<typename... Args>
wrapper(Args&&... args) : wrapped_value(std::forward<Args>(args)...) {
}
};
struct A {
int a;
A(int i = 0) : a(i) {
}
};
int main() {
wrapper<A> v1;
wrapper<A> v2(v1);
}
In the above template substitution is not failing as applied to the required the copy constructor. We end up with two overloads of the copy constructor, one generated by the compiler as part of special function generation and one produced by substituting the type of v1 into the constructor template:
wrapper(wrapper<A>& rhs); // (1) instantiated from the perfect forwarding template
wrapper(const wrapper<A>& rhs); // (2) compiler-generated ctor.
by the rules of C++ (1) has to be chosen since v1 in the orginal code is not const. You can actually check this by making it const and the program will no longer fail to compile.
As for what to do about this, as #jcai mentions in comments, Scott Meyers' Item 27 in Effective Modern C++ is about how to handle this issue -- basically it comes down to either not using perfect forwarding or using "tag dispatch" -- so I will not go into it here.
Related
I've got an interesting puzzle that I can't seem to completely solve. The following code is a snipped for my own function implementation. When I try to push_back a lambda into a vector of this function type, it should be converted to the function type. This seems to happen, but strangely the converting constructor is called an infinite amount of times. I tried to boil down the problem to the minimum example which I show below: It works when I either comment out the allocation of the lambda in the memory resource, the destructor or the operator() return value... But I can't find the commen denominator. I bet it's something stupid but I just can't find it.
Demo
#include <concepts>
#include <cstdio>
#include <memory_resource>
template <typename Fn, typename R, typename... Args>
concept invocable_r = std::is_invocable_r<R, Fn, Args...>::value;
template <typename R, typename... Args>
class function;
template <typename R, typename... Args>
class function<R(Args...)>
{
public:
using allocator_type = std::pmr::polymorphic_allocator<std::byte>;
auto get_allocator() {
return allocator_;
}
template <invocable_r<R, Args...> Cb>
function(Cb&& fn, allocator_type allocator = {})
: allocator_{ allocator }
{
printf("Converting constructor invoked!\n");
// Comment this out
mem_ptr_ = static_cast<void*>(allocator_.new_object<Cb>(std::forward<Cb>(fn)));
}
// Or this
~function() {}
auto operator()(Args... args) {
// or this
return R{};
}
private:
allocator_type allocator_;
void* mem_ptr_ = nullptr;
};
int main()
{
using foo_t = function<int()>;
std::vector<foo_t> myvec;
myvec.push_back([]() -> int { printf("Hello World1!\n"); return 10; });
}
Yields:
Converting constructor invoked!
Converting constructor invoked!
Converting constructor invoked!
Converting constructor invoked!
Converting constructor invoked!
Converting constructor invoked!
Converting constructor invoked!
Converting constructor invoked!
Converting constructor invoked!
Converting constructor invoked!
Converting constructor invoked!
... (inifinte)
The problem was that an invisible templated method
function(Cb&& fn, allocator_type allocator = {})
was instantiated as requested by vector::push_back() with the following signature:
template<>
inline function<function<int ()> >(function<int ()> && fn, allocator_type allocator)
As can be seen in cppinsights. But why is instantiated? Well, vector::push_back has an overload for const T& and T&& whereas T is the actual value type of the vector. So in order to call those functions, an object of type T must first be instantiated before the call which is done using the converting constructor instantiated with the lambda parameter like this:
myvec.push_back(function<int ()>(__lambda_134_21{}, allocator_type{}));
The resulting r-value reference of type function<int()> is then supposed to be plugged into the push_back method and forwarded to allocator::construct() within the internals of std::vector eventually, but now arises an interesting situation: Afaik what happens is that because the default move constructor of function is implicitely deleted as ~function() is defined, it can't actually pass function as an rvalue-reference and has to use push_back's const T& overload.
Here's something I still don't quite understand though: An rvalue-reference could actually be bound to const T&, but somehow the compiler decides to "create a new function instance from scratch" using the converting constructor with the temporary function()-object as template parameter, hence we get the above mentioned template instantiation. This however produces the same scenario again: We still have an rvalue reference, and the compiler thinks it better converts it again, and again, and again...
So the fact that function<> is itself invokable actually makes it viable for overloads with its own template which is something I should prevent against. This is also why excluding operator()() will work, because now function<> is not invokable anymore. As for why the inifinite calls don't happen when I exclude the allocator call within the converting constructor is still a mistery to me. Could be related to optimizations, but honestly idk, maybe someone can shed some light on this.
I need to make a wrapper class template that can possibly contain a reference member. I have both copy and move constructors defined, where references of the wrapped type are passed as arguments. This is all good if the type is not a reference. But if it is, both type& and type&& become lvalue references, and the two constructors conflict with each other.
I tried defining one set of constructors for the case where the type is not a reference and another for when it is, making use of the requires clause, as shown in the code below. But this doesn't compile if the type is a reference! GCC somehow treats the poisoned constructors as still valid.
Am I misunderstanding how requires works, or is this a bug? Here is the same example in the compiler explorer: https://godbolt.org/z/qjMxx3nGE
#include <type_traits>
template <typename T>
struct S {
using type = T;
type x;
S(const type& x) requires(!std::is_reference_v<type>)
: x(x) { }
S(type&& x) requires(!std::is_reference_v<type>)
: x(x) { }
S(type x) requires(std::is_reference_v<type>)
: x(x) { }
};
int main(int argc, char** argv) {
int i = 5;
S<int&> s(i);
return s.x;
}
Every function you define has to be distinct in some way - different name, different parameters, different constraints, etc.
For T=int&, your three constructors are:
S(int&) requires (!std::is_reference_v<int&>);
S(int&) requires (!std::is_reference_v<int&>);
S(int&) requires (std::is_reference_v<int&>);
The first two are the same, but there are two of them. That's an error. You need to differentiate these cases.
requires doesn't prevent the function from existing in the way that you might think - it just removes it as a candidate from overload resolution. It doesn't behave like a hypothetical if would:
if (std::is_reference_v<T>) {
S(T x);
} else {
S(T const&);
S(T&&);
}
It'd be easier to simply add a single constructor template that's constrained on being constructible:
template <typename U> requires std::constructible_from<T, U>
S(U&& u) : x(std::forward<U>(u)) { }
Or provide a specialization for S<T&> and split the constructors that way.
template<typename T>
class A
{
public:
A(T &t)
: t_(t){}
T t_;
};
int main()
{
int value;
A<decltype(value)> a(value);
// what I wish for : A a(value);
// which does not compile "missing template argument before 'a'"
}
Is there a way in the declaration of A (or somewhere else) to hint the compiler that T should be automatically resolved to the type passed to the constructor ?
(ideally c++11, but happy to hear about less old versions)
C++17 does it out of the box (or with the help of deduction guides), previous versions cannot.
As answered by #Quentin, this is only possible starting in C++17. However, if you're fine with calling a function to create your A objects, the following should do what you want in C++11:
template <class T, class NonRefT = typename std::remove_reference<T>::type>
A<NonRefT> create_A (T && t) {
return A<NonRefT>(std::forward<T>(t));
}
// Or even closer to your original code:
template <class T>
auto create_A (T && t) -> A<decltype(t)> {
return A<decltype(t)>(std::forward<T>(t));
}
I used std::remove_reference based on your use of decltype, though you might want to use std::decay instead.
int main () {
int value = 5;
auto a = create_A(value);
}
If I remember correctly the example code has an edge-case where it does not work as expected pre-C++17. The compiler will elide the copy/move constructor to create a from the rvalue returned by create_A(). However, it will check during compilation whether A's copy/move constructor (which it won't use) is available/accessible. Starting from C++17 the copy/move elision is done "properly" and no copy/move constructor is necessary for such code. (Also, I might be misremembering and it might be checking for copy/move assignment instead.)
In C++11 you can create a simple make_A function like this:
#include <iostream>
template <typename T>
class A {
public:
A(T &t) : t_(t) {}
T t_;
};
template <typename T>
A<T> make_A(T&& t) {
return A<T>(std::forward<T>(t));
}
int main() {
int value = 0;
auto a = make_A(value);
return 0;
}
Demo
This problem is based on code that works for me on GCC-4.6 but not for another user with CLang-3.0, both in C++0x mode.
template <typename T>
struct MyBase
{
//protected:
T m;
template <typename Args...>
MyBase( Args&& ...x ) : m( std::forward<Args>(x)... ) {}
};
An object of MyBase can take any list of constructor arguments, as long as T supports that construction signature. The problem has to do with the special-member functions.
IIUC, the constructor template cancels the automatically-defined default constructor. However, since the template can accept zero arguments, it will act as an explicitly-defined default constructor (as long as T is default-constructible).
IIUC, determination of a class' copy-construction policy ignores constructor templates. That means in this case that MyBase will gain an automatically-defined copy constructor (as long as T is copyable) that'll channel T copy-construction.
Apply the previous step for move-construction too.
So if I pass a MyBase<T> const & as the sole constructor argument, which constructor gets called, the forwarding one or the implicit copying one?
typedef std::vector<Int> int_vector;
typedef MyBase<int_vector> VB_type;
int_vector a{ 1, 3, 5 };
VB_type b{ a };
VB_type c{ b }; // which constructor gets called
My user's problem was using this in as a base class. The compiler complained that his class couldn't synthesize an automatically-defined copy constructor, because it couldn't find a match with the base class' constructor template. Shouldn't it be calling MyBase automatic copy-constructor for its own automatic copy-constructor? Is CLang in error for coming up with a conflict?
I'm just in the bar with Richard Corden and between us we concluded that the problem has nothing to do with variadic or rvalues. The implicitly generated copy construct in this case takes a MyBase const& as argument. The templated constructor deduced the argument type as MyBase&. This is a better match which is called although it isn't a copy constructor.
The example code I used for testing is this:
#include <utility>
#include <vector>i
template <typename T>
struct MyBase
{
template <typename... S> MyBase(S&&... args):
m(std::forward<S>(args)...)
{
}
T m;
};
struct Derived: MyBase<std::vector<int> >
{
};
int main()
{
std::vector<int> vec(3, 1);
MyBase<std::vector<int> > const fv1{ vec };
MyBase<std::vector<int> > fv2{ fv1 };
MyBase<std::vector<int> > fv3{ fv2 }; // ERROR!
Derived d0;
Derived d1(d0);
}
I needed to remove the use of initializer lists because this isn't supported by clang, yet. This example compiles except for the initialization of fv3 which fails: the copy constructor synthesized for MyBase<T> takes a MyBase<T> const& and thus passing fv2 calls the variadic constructor forwarding the object to the base class.
I may have misunderstood the question but based on d0 and d1 it seems that both a default constructor and a copy constructor is synthesized. However, this is with pretty up to date versions of gcc and clang. That is, it doesn't explain why no copy constructor is synthesized because there is one synthesized.
To emphasize that this problem has nothing to do with variadic argument lists or rvalues: the following code shows the problem that the templated constructor is called although it looks as if a copy constructor is called and copy constructors are never templates. This is actually somewhat surprising behavior which I was definitely unaware of:
#include <iostream>
struct MyBase
{
MyBase() {}
template <typename T> MyBase(T&) { std::cout << "template\n"; }
};
int main()
{
MyBase f0;
MyBase f1(const_cast<MyBase const&>(f0));
MyBase f2(f0);
}
As a result, adding a variadic constructor as in the question to a class which doesn't have any other constructors changes the behavior copy constructors work! Personally, I think this is rather unfortunate. This effectively means that the class MyBase needs to be augmented with copy and move constructors as well:
MyBase(MyBase const&) = default;
MyBase(MyBase&) = default;
MyBase(MyBase&&) = default;
Unfortunately, this doesn't seem to work with gcc: it complains about the defaulted copy constructors (it claims the defaulted copy constructor taking a non-const reference can't be defined in the class definition). Clang accepts this code without any complaints. Using a definition of the copy constructor taking a non-const reference works with both gcc and clang:
template <typename T> MyBase<T>::MyBase(MyBase<T>&) = default;
I've personally had the problem with GCC snapshots for quite some time now. I've had trouble figuring out what was going on (and if it was allowed at all) but I came to a similar conclusion as Dietmar Kühl: the copy/move constructors are still here, but are not always preferred through the mechanics of overload resolution.
I've been using this to get around the problem for some time now:
// I don't use std::decay on purpose but it shouldn't matter
template<typename T, typename U>
using is_related = std::is_same<
typename std::remove_cv<typename std::remove_reference<T>::type>::type
, typename std::remove_cv<typename std::remove_reference<U>::type>::type
>;
template<typename... T>
struct enable_if_unrelated: std::enable_if<true> {};
template<typename T, typename U, typename... Us>
struct enable_if_unrelated
: std::enable_if<!is_related<T, U>::value> {};
Using it with a constructor like yours would look like:
template<
typename... Args
, typename = typename enable_if_unrelated<MyBase, Args...>::type
>
MyBase(Args&&... args);
Some explanations are in order. is_related is a run off the mill binary trait that checks that two types are identical regardless of top-level specifiers (const, volatile, &, &&). The idea is that the constructors that will be guarded by this trait are 'converting' constructors and are not designed to deal with parameters of the class type itself, but only if that parameter is in the first position. A construction with parameters e.g. (std::allocator_arg_t, MyBase) would be fine.
Now I used to have enable_if_unrelated as a binary metafunction, too, but since it's very convenient to have perfectly-forwarding variadic constructors work in the nullary case too I redesigned it to accept any number of arguments (although it could be designed to accept at least one argument, the class type of the constructor we're guarding). This means that in our case if the constructor is called with no argument it is not SFINAE'd out. Otherwise, you'd need to add a MyBase() = default; declaration.
Finally, if the constructor is forwarding to a base another alternative is to inherit the constructor of that base instead (i.e. using Base::Base;). This is not the case in your example.
I upvoted Dietmar's answer because I totally agree with him. But I want to share a "solution" I was using some time earlier to avoid these issues:
I intentionally added a dummy parameter to the variadic constructor:
enum fwd_t {fwd};
template<class T>
class wrapper
{
T m;
public:
template<class...Args>
wrapper(fwd_t, Args&&...args)
: m(std::forward<Args>(args)...)
{}
};
:::
int main()
{
wrapper<std::string> w (fwd,"hello world");
}
Especially since the constructor would accept anything without this dummy parameter, it seems appropriate to make user code explicitly choose the correct constructor by (sort of) "naming" it.
It might not be possible in your case. But sometimes you can get away with it.
Here is a minimal code that shows the problem:
template<typename To, typename From> To convert(const From& x);
struct A
{
int value;
template<typename T> A(const T& x) { value = convert<A>(x).value; }
};
struct B : public A { };
int main()
{
B b;
A a = b;
}
It gives me: undefined reference to 'A convert<A, B>(B const&)'
As expected, as I removed the default copy constructor. But if I add this line to A:
A(const A& x) { value = x.value; }
I get the same error. If I try to do this way: (adding a template specialization)
template<> A(const A& x) { value = x.value; }
I get: error: explicit specialization in non-namespace scope 'struct A'.
How to solve it?
My compiler is MinGW (GCC) 4.6.1
EDIT:
The convert functions converts from many types to A and back again. The problem is that don't make sense writing a convert function from B to A because of the inheritance. If I remove the line that calls convert from A it just works. The idea is to call convert for all times that do't inherit from A, for these, the default constructor should be enough.
As for as I understand, when b is passed, as b is not an object of A, copy constructor is not called, instead the template constructor is called.
However, if an object of the derived class is passed, you want the copy constructor of A to be called.
For this, there is one solution using <type_traits> (c++0x):
#include <type_traits>
template<typename To, typename From> To convert(const From& x);
struct A
{
int value;
template<typename T> A(const T& x,
const typename std::enable_if<!std::is_base_of<A,T>::value, bool>::type = false)
{ value = convert<A>(x).value; }
A(){}
};
struct B : public A { };
int main()
{
B b;
A a = b;
}
The template is disabled why an object of a class derived from A is passed, so the only available constructor is the copy constructor.
You can solve it by defining the convert function :
template<typename To, typename From> const To& convert(const From& x)
{
return x;
}
As expected, as I removed the default copy constructor.
No; while you do need to replace the default copy constructor, its omission causes a different sort of problem (and only if you have the sort of calling code that needs it).
The error you report:
undefined reference to 'A convert<A, B>(B const&)
is a linker error. It is telling you that you don't actually have a convert function anywhere.
error: explicit specialization in non-namespace scope 'struct A'
You were right the first time about how to put back the default copy constructor. However, that's still irrelevant to the linker error.
Well, the "undefined reference" should be easily solved by linking in the implementation of the function!
NOTE: as things stand, returning a copy will trigger a stack overflow.
EDIT: IMO, your design is flawed, you have moved the construction logic outside of A into convert; rather than this, you should provide specific conversion constructors in A. It's either that or some boost::enable_if trick to disable the conversion constructor if the passed in type is derived from A (you can use one of the type_traits for that). For example, if you can construct an A from an int, provide the specific constructor in A itself.