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.
Related
Example:
template<class T> class A{
public:
A(){}
template<class U> A(A<U> &){}
private:
template<class U> A(A<U>&&){}
};
int main() {
A<int> a1;// legal
A<int> a2(std::move(a1));//legal. it calls implicitly-declared constructor.
}
but when I delete A(){}:
template<class T> class A{
public:
template<class U> A(A<U> &){}
private:
template<class U> A(A<U>&&){}
};
int main() {
A<int> a1;// illegal. 3
A<int> a2(std::move(a1));
}
If template constructors don't influence implicitly-declared-rules. why does it become illegal?
If template constructors do influence implicitly-declared-rules, why isn't A<int> a2(std::move(a1)); illegal in the first example?
Tested in gcc and clang on ubuntu.
In
template<class T> class A{
public:
A(){} // A
template<class U> A(A<U> &){} // B
private:
template<class U> A(A<U>&&){} // C
};
int main() {
A<int> a1; // 1
A<int> a2(std::move(a1)); // 2
}
line 1 calls constructor A. Line 2 however doesn't call constructor A, B, or C. Since your class does not declare a copy constructor (B is not a copy constructor because it is a template) the compiler creates a default move constructor (C is not a move constructor because it is a template) and it is that default move constructor that line 2 uses.
In you second example, the presence of B and C stops the compiler from generating a default constructor so line 1 no longer compiles, but if it did, line 2 would still compile because it would use the compiler generated move constructor.
So, the rule here that is tripping you up is a copy or move constructor is never a template constructor, but constructor templates still count as constructors, so they disable the default constructor. That means you need to ignore them when checking to see if your class will have a default copy or move constructor generated. For the rules on when you get which see: Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator?
Suppose I'm writing a class template C<T> that holds a T value, so C<T> can be copyable only if T is copyable. Normally, when a template might or might not support a certain operation, you just define the operation, and it's up to your callers to avoid calling it when it's not safe:
template <typename T>
class C {
private:
T t;
public:
C(const C& rhs);
C(C&& rhs);
// other stuff
};
However, this creates problems in the case of a copy constructor, because is_copy_constructible<C<T>> will be true even when T is not copyable; the trait can't see that the copy constructor will be ill-formed if it's called. And that's a problem because, for example, vector will sometimes avoid using the move constructor if std::is_copy_constructible is true. How can I fix this?
I believe is_copy_constructible will do the right thing if the constructor is explicitly or implicitly defaulted:
template <typename T>
class C {
private:
T t;
public:
C(const C& rhs) = default;
C(C&& rhs) = default;
// other stuff
};
However, it's not always possible to structure your class so that defaulted constructors will do the right thing.
The other approach I can see is to use SFINAE to conditionally disable the copy constructor:
template <typename T>
class C {
private:
T t;
public:
template <typename U = C>
C(typename std::enable_if<std::is_copy_constructible<T>::value,
const U&>::type rhs);
C(C&& rhs);
// other stuff
};
Aside from being ugly as sin, the trouble with this approach is that I have to make the constructor a template, because SFINAE only works on templates. By definition, copy constructors are not templates, so the thing I'm disabling/enabling isn't actually the copy constructor, and consequently it won't suppress the copy constructor that's implicitly provided by the compiler.
I can fix this by explicitly deleting the copy constructor:
template <typename T>
class C {
private:
T t;
public:
template <typename U = C>
C(typename std::enable_if<std::is_copy_constructible<T>::value,
const U&>::type rhs);
C(const C&) = delete;
C(C&& rhs);
// other stuff
};
But that still doesn't prevent the copy constructor from being considered during overload resolution. And that's a problem because all else being equal, an ordinary function will beat a function template in overload resolution, so when you try to copy a C<T>, the ordinary copy constructor gets selected, leading to a build failure even if T is copyable.
The only approach I can find that in principle will work is to omit the copy constructor from the primary template, and provide it in a partial specialization (using more SFINAE trickery to disable it when T is not copyable). However, this is brittle, because it requires me to duplicate the entire definition of C, which creates a major risk that the two copies will fall out of sync. I can mitigate this by having the method bodies share code, but I still have to duplicate the class definitions and the constructor member-init lists, and that's plenty of room for bugs to sneak in. I can mitigate this further by having them both inherit from a common base class, but introducing inheritance can have a variety of unwelcome consequences. Furthermore, public inheritance just seems like the wrong tool for the job when all I'm trying to do is disable one constructor.
Are there any better options that I haven't considered?
A noteworthy approach is partial specialization of the surrounding class template.
template <typename T,
bool = std::is_copy_constructible<T>::value>
struct Foo
{
T t;
Foo() { /* ... */ }
Foo(Foo const& other) : t(other.t) { /* ... */ }
};
template <typename T>
struct Foo<T, false> : Foo<T, true>
{
using Foo<T, true>::Foo;
// Now delete the copy constructor for this specialization:
Foo(Foo const&) = delete;
// These definitions adapt to what is provided in Foo<T, true>:
Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;
Foo& operator=(Foo const&) = default;
};
This way the trait is_copy_constructible is satisfied exactly where T is_copy_constructible.
However, it's not always possible to structure your class so that defaulted constructors will do the right thing.
It's usually possible with enough effort.
Delegate the work that can't be done by a defaulted constructor to another member, or wrap the T member in some wrapper that does the copying, or move it into a base class that defines the relevant operations.
Then you can define the copy constructor as:
C(const C&) = default;
Another way to get the compiler to decide whether the default definition should be deleted or not is via a base class:
template<bool copyable>
struct copyable_characteristic { };
template<>
struct copyable_characteristic<false> {
copyable_characteristic() = default;
copyable_characteristic(const copyable_characteristic&) = delete;
};
template <typename T>
class C
: copyable_characteristic<std::is_copy_constructible<T>::value>
{
public:
C(const C&) = default;
C(C&& rhs);
// other stuff
};
This can be used to delete operations using arbitrary conditions, such as is_nothrow_copy_constructible rather than just a straightforward T is copyable implies C is copyable rule.
Update for C++20
In C++20, this is extremely straightforward: you can add a requires to your copy constructor:
template <typename T>
class C {
public:
C(const C& rhs) requires some_requirement_on<T>
{
...
}
};
The below solution actually isn't great, because it reports the type as being copyable for all traits - even if it actually isn't.
If you want to conditionally disable your copy constructor, you definitely want it to participate in overload resolution - because you want it to be a loud compile error if you try to copy it.
And to do that, all you need is static_assert:
template <typename T>
class C {
public:
C(const C& rhs) {
static_assert(some_requirement_on<T>::value,
"copying not supported for T");
}
};
This will allow copy construction only if some_requirement_on<T> is true, and if it's false, you can still use the rest of the class... just not copy construction. And if you do, you'll get a compile error pointing to this line.
Here's a simple example:
template <typename T>
struct Foo
{
Foo() { }
Foo(const Foo& ) {
static_assert(std::is_integral<T>::value, "");
}
void print() {
std::cout << "Hi" << std::endl;
}
};
int main() {
Foo<int> f;
Foo<int> g(f); // OK, satisfies our condition
g.print(); // prints Hi
Foo<std::string> h;
//Foo<std::string> j(h); // this line will not compile
h.print(); // prints Hi
}
template <typename T>
class variant {
struct moo {};
public:
variant(const variant& ) = default;
variant(std::conditional_t<!std::is_copy_constructible<T>::value,
const variant&, moo>,
moo=moo());
variant() {};
};
This makes a non-eligible template instance have two copy constructors, which makes it not copy constructible.
This is a bit of a trick, but it works.
template<bool b,class T>
struct block_if_helper{
using type=T;
};
template<class T>
struct block_if_helper<true, T>{
class type{
type()=delete;
};
};
template<bool b,classT>
using block_if=typename block_if_helper<b,T>::type;
template<bool b,classT>
using block_unless=typename block_if_helper<!b,T>::type;
now we create a method that is your copy ctor ... maybe.
template<class X>
struct example {
enum { can_copy = std::is_same<X,int>{} };
example( block_unless<can_copy, example>const& o ); // implement this as if `o` was an `example`
// = default not allowed
example( block_if<can_copy, example>const& )=delete;
};
and now the =default is the copy ctor if and only if can_copy, and the =delete of not. The stub type that it is otherwise cannot be created.
I find this technique useful for general method disabling on compilers that do not support the default template argument feature, or for methods (like virtual or special) that cannot be templates.
C::C(C const& rhs, std::enable_if<true, int>::type dummy = 0) is also a copy ctor because the second argument has a default value.
I need to elaborate the constructor of the following class:
class Foo {
public:
const std::vector<Bar> bars;
Foo(int num_bars, ...);
}
Assume for the sake of discussion that the extra argument are all const Bar& or just Bar.
I need to initialize v on construction with the bars in the va_list. How can I do that?
You can't. Make Foo a variadic template instead:
template <typename ...ArgumentTypes>
Foo(ArgumentTypes&& args...):
bars({std::forward<ArgumentTypes>(args)...})
{
}
This uses the initializer-list constructor of the vector.
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.
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);
}