How template influence implicitly-declared-rules in C++? - c++

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?

Related

Disable default copy construct & assign constructor when using templated constructors

I'm trying to create a class which is copyable dependent on its template parameter (bool Copyable) otherwise it is move only.
It shall be constructible from the type itself (default constructor) through myclass(myclass&&) and myclass(myclass const&) when enabled through the template parameter bool Copyable.
Also it shall be constructible from myclass with other template arguments, which is covered by my current implementation through the templated constructors and assignment operators.
The rule of zero is used here to generate the default constructors and assignment operators through the inherited copyable helper struct which disables the copy constructor and copy assignment operator when bool Copyable is false.
template<bool>
struct copyable { };
template <>
struct copyable<false>
{
// Disables copy construct & copy assign
copyable() = default;
copyable(copyable const&) = delete;
copyable(copyable&&) = default;
copyable& operator=(copyable const&) = delete;
copyable& operator=(copyable&&) = default;
};
template<typename A, typename B, typename C>
struct storage_t
{
// Implementation depends on A, B and C
};
template<typename A, typename B, typename C, bool Copyable>
class myclass
: public copyable<Copyable>
{
storage_t<A, B, C> _storage;
public:
// It should generate the default constructors and
// assignment operatos dependent on its inherited helper struct copyable.
// Comment this out to disable the error...
// (Implementation omitted)
template<typename A, typename B, typename C>
myclass(myclass<A, B, C, true> const&) { }
template<typename A, typename B, typename C, bool Copyable>
myclass(myclass<A, B, C, Copyable>&&) { }
template<typename A, typename B, typename C>
myclass& operator= (myclass<A, B, C, true> const&) { return *this; }
template<typename A, typename B, typename C, bool Copyable>
myclass& operator= (myclass<A, B, C, Copyable>&&) { return *this; }
// ... comment end
};
By interpreting earlier answers at stackoverflow like:
Why does template copy constructor override default copy constructor?
Copy constructor of template class
Why can't I override the default copy constructor and assignment operator with template versions in C++
Which says:
The compiler will still generates a default copy-constructor for you, instead of instantiating the templated constructor.
I thought that the compiler still generates a default constrctor although there is a templated constructor provided.
But the compilation of the upper example code fails with the error message (msvc 2015):
error C2512: 'myclass': no appropriate default constructor available:
myclass<int, int, int, true> mc1;
When i outcomment the provided templated constructors and assignment operators the default constructors are used but the capability is lost to assign a myclass with other template parameters.
A simple usage example would be:
/////
// Testing the copyable class
myclass<int, int, int, true> mc1;
// Copy construct
myclass<int, int, int, true> mc2(mc1);
// Copy assign
mc1 = mc2;
/////
// Testing the move only class
myclass<int, int, int, false> mc3;
// Move construct
myclass<int, int, int, false> mc4(std::move(mc3));
// Move assign
mc3 = std::move(mc4);
// Not working part:
// Copy and move from myclass with other template params
myclass<int, int, float, true> mc5;
// Error here:
mc1 = mc5;
Is there a way to disable the copy construct and assignment operators as described through a template parameter and also provide templated constructors/assignment operators?
Default constructor is not generated if you provide other constructors.
Just provide a default one:
myclass() = default;
In your case, copy constructor is still generated if possible (which is not the case, when you inherit of copyable<false>).

Can I disable copy construction of a class based on the properties of it's template parameters? [duplicate]

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.

Conditionally disabling a copy constructor

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.

Template class constructor takes instance of template class

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.

Conflict between copy constructor and forwarding constructor

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.