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.
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?
Is there a way to encapsulate a type in a template class (akin to something like std::optional) that has all the necessary special constructors and assignment operators (i.e. copy ctor/assignment, move ctor/assignment) but only "enables" them if the underlying type has those functions? The functions in type_traits like std::is_copy_constructible look like they might help but I'm not sure how to use them to achieve this goal. For reference, the type I'm trying to implement is similar to std::optional but instead of the alternate value being simply "none" I want to use a custom error type. E.g.
template <typename T>
class ErrorOr {
public:
enum class Error {
FATAL,
WARNING,
NONE,
};
ErrorOr(T val) : val(val), error(Error::NONE) {}
ErrorOr(Error error) : error(error) {}
// TODO: Implement copy/move ctors/assignment operators that only
// exist if they do for the underlying T
T get() { val; }
private:
T val;
Error error;
};
This is a very bare-bones/minimal implementation that doesn't have a lot of the necessary features but hopefully illustrates the point I'm trying to make.
Is this possible in C++11?
In this case, do nothing. ErrorOr<T> having a member of type T will default all the special member functions to do the Right Thing. If T isn't copyable, ErrorOr<T> won't be either.
However, this also isn't really an optional type since you always have a T. If you eventually move to an implementation which conditionally has a T,
one way to do this would be to inherit from an empty type that either enables or disables the special members as appropriate.
A simplified version would be:
template <bool allowCopies>
struct copy_enabler {
copy_enabler() = default;
copy_enabler(copy_enabler const& ) = default;
copy_enabler& operator=(copy_enabler const& ) = default;
};
template <>
struct copy_enabler<false> {
copy_enabler() = default;
copy_enabler(copy_enabler const& ) = delete;
copy_enabler& operator=(copy_enabler const& ) = delete;
};
Then you can simply:
template <typename T>
class ErrorOr : private copy_enabler</* is T copyable */> { ... };
In practice, you will want to do this for all the special member functions, and also add a tag type so that if you use this trick for multiple different class templates, they don't end up sharing a common base.
In the following code, class X has a copy constructor defined only if its template argument is of a class type and is copy-constructible (which means that it should have copy constructor):
template <typename T>
class X {
static constexpr bool has_copy_constructor
= std::is_class<T>::value && std::is_copy_constructible<T>::value;
struct dummy_type { };
public:
X() = default;
X(typename std::conditional<has_copy_constructor,
const X &, const dummy_type &>::type) { std::cerr << "copy ctor\n"; }
X(typename std::conditional<has_copy_constructor,
const dummy_type &, const X &>::type) = delete;
};
int main() {
X<std::string> x1;
X<std::string> x2(x1); // OK
X<int> x3;
X<int> x4(x3); // ERROR
}
Not that I like this solution, but it kind of works.
Yes, use std::enable_if (std::enable_if_t in newer c++ versions, not sure if that was added in c++11).
template<typename T>
struct my_type
: T
{
my_type(T && val, typename std::enable_if<std::is_move_constructible<T>::value>::type * = nullptr)
: T(std::move(val))
{
}
};
Please consider the following tree class
template<typename T>
class tree
{
public:
template<typename U>
tree(U&& value)
: m_value(std::forward<U>(value))
{ }
private:
T m_value;
};
and some verbose class foo which is constructed by its std::string name which is printed via std::cout whenever one of its constructors is invoked.
foo a("a"), b("b");
foo const c("c");
tree<foo> s(std::move(a)), t(b), u(c);
yields the following output:
a constructed
b constructed
c constructed
a moved
b copied
c copied
which is as expected.
How does this work?
To be specific: We cannot use
tree(T&& value)
: m_value(std::forward<T>(value))
{ }
Why? Well, cause we force decltype(value) to be foo&& which is not the case in t(b) and u(c). We would need to provide
tree(T const& value)
: m_value(value)
{ }
as well, if we want to use this arguable variant. The ctor used in the tree implementation works cause of the reference collapsing rules.
But why can't we use
template<typename U>
tree(U&& value)
: m_value(std::forward<T>(value))
{ }
instead?
Well, the reason is that in u(c) we have U = foo const& = decltype(value), but std::forward<T> only accepts foo& or foo&&, since T = foo. In contrast, std::forward<U> accepts foo const& (or foo const&&), since U = foo const&.
So, if I'm not overlooking something, we should use template<typename U> tree(U&&) instead of a tree(T const&), tree(T&&) combination. But: The compiler will take the template<typename U> tree(U&&) ctor in push_back, where the intend is that the move ctor of tree is taken:
foo a("a"), b("b");
foo const c("c");
tree<foo> t("root");
t.emplace_back(std::move(a));
t.emplace_back(b);
t.emplace_back(c);
tree<foo> u("other root");
u.push_back(t); // cannot convert argument 1 from 'tree<foo>' to 'std::string const&'
What can I do? Do I need to use the tree(T const&), tree(T&&) combination instead?
[Sorry for being a bit too verbose, but I felt responsible for clarifying any technical issue before asking for design.]
A greedy constructor such as template<typename U> tree(U&&) must be suitably constrained, or you'll have a lot of problems down the road. You saw it hijack copy construction from a non-const lvalue because it's a better match than the copy constructor. It also defines an implicit conversion from everything under the sun, which can have "fun" effects on overload resolution.
A possible constraint might be "accept only things that are convertible to T":
template<typename U, class = std::enable_if_t<std::is_convertible_v<U, T>>>
tree(U&& u) : m_value(std::forward<U>(u)) {}
Or perhaps "accept only things that are convertible to T and not a tree".
template<class U>
using not_me = std::negation<std::is_same<U, tree>>;
template<typename U,
class = std::enable_if_t<std::conjunction_v<not_me<std::decay_t<U>>,
std::is_convertible<U, T>>>>
tree(U&& u) : m_value(std::forward<U>(u)) {}
You can do both.
If you go with:
template<typename U>
tree(U&& value)
: m_value(std::forward<U>(value))
{ }
the downside is that any one-argument constructor of T can be invoked this way. This may not be what you want. For example, with that variant, the following is valid:
struct foo
{
foo() = default;
foo(const foo &ref) = default;
explicit foo(int);
};
tree<foo> t(10);
t = 20;
This is a decision you need to find for yourself, however, I personally see that as a huge downside. I would make that constructor explicit (eliminating the second initialisation) and go for tree(const T&) along with tree(T&&) to avoid the first initialisation.
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 have the following class:
template <typename T>
class Fixed2DContainer {
T* ptr;
public:
const int total_cols;
const int total_rows;
Fixed2DContainer(int cols, int rows);
T& operator()(int col_n, int row_n);
~Fixed2DContainer();
private : //disallow copy
Fixed2DContainer& operator=(const Fixed2DContainer&);
Fixed2DContainer operator()(const Fixed2DContainer&);
};
Now I'd like to specialize this template for some class so that the only change is that I can have an another constructor.
Basically I want to be able to do:
Fixed2DContainer<Image>("filename.jpg");
is there an elegant way to do this? I am fairly new to template so i have no idea of the difficulty
If your compiler supports it, C++11 has inheriting constructors that will pretty much get you where you want to be:
template <typename T>
class Base {}; // has all the implementation
template <typename T>
class Template {
using Base::Base;
//Template() = delete; // You might need this if you don't want
// the default constructor
};
template <>
class Template<int> {
using Base::Base;
Template( std::string const & x ) {}
//Template() = delete; // You might need this if you don't want
// the default constructor
};
Here's a quick example of what I meant in my earlier comment ...
template <typename T>
class Fixed2DContainerBase {
T* ptr;
public:
const int total_cols;
const int total_rows;
Fixed2DContainerBase(int cols, int rows);
T& operator()(int col_n, int row_n);
~Fixed2DContainerBase();
private : //disallow copy
Fixed2DContainerBase& operator=(const Fixed2DContainerBase&);
Fixed2DContainerBase(const Fixed2DContainerBase&);
};
// primary template
template <typename T>
class Fixed2DContainer : public Fixed2DContainerBase<T> {
Fixed2DContainer(int cols, int rows);
~Fixed2DContainer();
};
// explicit specialization
template <>
class Fixed2DContainer<Image> : public Fixed2DContainerBase<Image> {
Fixed2DContainer(int cols, int rows);
Fixed2DContainer(const std::string&);
~Fixed2DContainer();
};
N.B. because the base class is non-copyable the derived classes will be too. It may not be necessary to define a destructor in the derived classes if all the cleanup can be done by the base destructor.
I have had the same problem myself. Unfortunately, every constructor must exist in the generic template. You can avoid people using the wrong constructor at runtime though;
template <typename T>
class Fixed2DContainer {
// omitted
Fixed2DContainer(string fileName)
{
// Do not allow construction of Fixed2DContainer with
// string argument in general case
ASSERT(false);
// or
throw new ExceptionType("Not implemented");
}
// omitted
};
template<>
class Fixed2DContainer<Image> {
// omitted
Fixed2DContainer(string fileName)
{
// Actual construction code
}
// omitted
};
Asserts are preferred because your code will break on the assert whereas the in the exception case it will break on the catch, making it slightly harder to debug.