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.
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 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.
I am using a class that encapsulates an std::variant of template specializations, such as:
template<typename Type> struct Generic_node {...};
struct Leaf_node : Generic_node<...> {...};
struct Inner_node : Generic_node<...> {...};
struct Node {std::variant<Leaf_node, Inner_node> variant_;};
I am trying to construct a Node from a function in Generic_node using a converting move constructor, but the compilation fails.
I defined a template constructor, which accepts an rvalue reference (assuming one of the specialized classes) and constructs the variant by moving the value to the variant, where I expect to call the converting move constructor # (4).
When i was trying to create minimal non-working example, i found that the problem really shows only from the template function, where if I knew the exact type (Leaf_node == Generic_node<...> which the compiler knows), the move construction would succeed. Therefore I assume there is as always some template magic happening that I did not anticipate.
#include <variant>
template<typename T>
struct Base
{
void f();
};
struct Derived : Base<int> {};
struct Variant
{
// In real program expecting the T to be one of the multiple variants
// Here I use only one variant because it suffices to illustrate the problem
template<typename T>
Variant(T&& t)
:
variant_ {std::move(t)}
{
}
std::variant<Derived> variant_;
};
template<typename T>
void
Base<T>::
f()
{
Variant {std::move(Derived {})}; // can call on exact type
Variant {std::move(Base<T> {})}; // can not call on specialized template type
}
int
main()
{
Derived {}.f();
}
Relevant compiler error message (clang 7, libstdc++-8):
note: candidate template ignored: substitution failure [with _Tp = Base<int>,
$1 = void, $2 = void]: implicit instantiation of undefined template
'std::variant<Derived>::__to_type_impl<18446744073709551615, false>'
variant(_Tp&& __t)
The problem most probably does not have anything to do with variants, but with the equality of Base<T> == Derived in the template instantiation of the Variant constructor, which the compiler as if did not see.
What is happening in the template instantiations, why can't the compiler call the provided constructor?
Edit: Since I intended to create a specialization, I forgot that inheritance can not imply class type equality, even if it technically is in this special case. This is therefore an easy task of constructing Derived by move from the specialized Base:
struct Derived : Base<int>
{
Derived() = default;
Derived(Base<int>&&) {}
};
If I am correct, the constructor needs to be explicitly defined for each derived class of Base.
In the example you are giving, the Derived class is a separate class from Base. They have exactly the same members, same methods, but they are still separate classes.
Easiest way to solve it would be to use a using statement instead of declaring it as a separate class:
using Derived = Base<int>;
I have a situation as follows:
Class Bar {
...
}
template <class T>
class Foo {
public:
...
Foo(Foo<Bar> bar) {
...
}
...
}
So one of the constructors of class Foo can take an element of class Foo parameterized by Bar. This is all fine until I instantiate something of class Foo parameterized by Bar where this constructor is interpreted as a copy constructor which isn't what I want. I'm wondering how I can have a constructor taking such an element without interfering with the copy constructor. For example I can do this:
template <class T>
class Foo {
public:
...
Foo(Foo<Bar> bar, int unused) {
...
}
...
}
And it works fine because now the constructor doesn't conflict with the copy constructor. Is there a standard way to deal with this problem?
If your class template doesn't need to declare a copy constructor (in the general case), you can declare the constructor as
Foo(Foo<Bar> const& bar);
which will be a converting constructor in general and a copy constructor for Foo<Bar>. Other specializations will use an implicitly declared copy constructor where applicable.
Otherwise, making a constructor a template ensures that it's not a special member. That being said, care has to be taken for that constructor not to interfere with the copy constructor. It's not nice, but you can do this:
template<typename U>
Foo(U bar, typename boost::enable_if<boost::is_same<U, Foo<Bar> > >::type* = 0);
This is a constructor that is not a copy constructor, and will only ever be used when passed arguments of type Foo<Bar>. Do note that due to the rules of overload resolution, the copy constructor of Foo<Bar> will be preferred over this constructor.
The preceding is for C++03. Here's a simple C++11 solution:
template<int = 0>
Foo(Foo<Bar>);
This is in fact not a valid copy constructor as copy constructors should follow one of the following four formats:
MyClass( const MyClass& other );
MyClass( MyClass& other );
MyClass( volatile const MyClass& other );
MyClass( volatile MyClass& other );
In order to have everything working as expected pass your parameter as a pointer:
Foo(Foo<Bar>* bar) {
// Logic
}
If you pass it by value you can very well create an infinite loop if your logic mimics a copy constructor.
I wish to have a non-template class with a template constructor with no arguments.
As far as I understand, it's impossible to have it (because it would conflict with the default constructor - am I right?), and the workaround is the following:
class A{
template <typename U> A(U* dummy) {
// Do something
}
};
Maybe there is a better alternative for this (or a better workaround)?
There is no way to explicitly specify the template arguments when calling a constructor template, so they have to be deduced through argument deduction. This is because if you say:
Foo<int> f = Foo<int>();
The <int> is the template argument list for the type Foo, not for its constructor. There's nowhere for the constructor template's argument list to go.
Even with your workaround you still have to pass an argument in order to call that constructor template. It's not at all clear what you are trying to achieve.
You could use a templated factory function instead of a constructor:
class Foo
{
public:
template <class T> static Foo* create() // could also return by value, or a smart pointer
{
return new Foo(...);
}
...
};
As far as I understand, it's impossible to have it (because it would conflict with the default constructor - am I right?)
You are wrong. It doesn't conflict in any way. You just can't call it ever.
template<class...>struct types{using type=types;};
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
the above helpers let you work with types as values.
class A {
template<class T>
A( tag<T> );
};
the tag<T> type is a variable with no state besides the type it caries. You can use this to pass a pure-type value into a template function and have the type be deduced by the template function:
auto a = A(tag<int>{});
You can pass in more than one type:
class A {
template<class T, class U, class V>
A( types<T,U,V> );
};
auto a = A(types<int,double,std::string>{});
Some points:
If you declare any
constructor(including a templated
one), the compiler will refrain from
declaring a default constructor.
Unless you declare a copy-constructor (for class X one
that takes X or X& or X const
&) the compiler will generate the
default copy-constructor.
If you provide a template constructor for class X which takes
T const & or T or T& then the
compiler will nevertheless generate a
default non-templated
copy-constructor, even though you may think that it shouldn't because when T = X the declaration matches the copy-constructor declaration.
In the latter case you may want to provide a non-templated copy-constructor along with the templated one. They will not conflict. When X is passed the nontemplated will be called. Otherwise the templated
HTH
You could do this:
class C
{
public:
template <typename T> C(T*);
};
template <typename T> T* UseType()
{
static_cast<T*>(nullptr);
}
Then to create an object of type C using int as the template parameter to the constructor:
C obj(UseType<int>());
Since you can't pass template parameters to a constructor, this solution essentially converts the template parameter to a regular parameter. Using the UseType<T>() function when calling the constructor makes it clear to someone looking at the code that the purpose of that parameter is to tell the constructor what type to use.
One use case for this would be if the constructor creates a derived class object and assigns it to a member variable that is a base class pointer. (The constructor needs to know which derived class to use, but the class itself doesn't need to be templated since the same base class pointer type is always used.)
Here's a workaround.
Make a template subclass B of A. Do the template-argument-independent part of the construction in A's constructor. Do the template-argument-dependent part in B's constructor.
It is perhaps easier and more intuitive to rely on std::in_place_type_t<T> which is used in std::variant, std::any, etc for exactly the same purpose:
#include <utility>
class A {
template <typename U>
A(std::in_place_type_t<U>) {
// Do something
}
};
A a(std::in_place_type_t<MyType>{});
try doing something like
template<class T, int i> class A{
A(){
A(this)
}
A( A<int, 1>* a){
//do something
}
A( A<float, 1>* a){
//do something
}
.
.
.
};
Just simple to add a dummy variable like
class A {
template<typename T>
A(const T&, int arg1, int arg2);
}