问题
Under certain conditions, I'd like to SFINAE away the copy constructor and copy assignment operator of a class template. But if I do so, a default copy constructor and a default assignment operator are generated. The SFINAE is done based on tags I pass as class template parameters. The problem is, that SFINAE only works on templates and a copy constructor/assignment operator can't be a template. Does there exist a workaround?
回答1:
This solution uses a base class that is conditionally not copyable (by explicitely marking the copy constructor and copy assignment operator as deleted).
template <bool>
struct NoCopy;
template <>
struct NoCopy<true>
{
// C++11 and later: marking as deleted. Pre-C++11, make the copy stuff private.
NoCopy(const NoCopy&) = delete;
NoCopy& operator=(const NoCopy&) = delete;
protected:
~NoCopy() = default; // prevent delete from pointer-to-parent
};
template <>
struct NoCopy<false>
{
// Copies allowed in this case
protected:
~NoCopy() = default; // prevent delete from pointer-to-parent
};
Example usage:
template <typename Number>
struct Foo : NoCopy<std::is_integral<Number>::value>
{
Foo() : NoCopy<std::is_integral<Number>::value>{}
{
}
};
int main()
{
Foo<double> a;
auto b = a; // compiles fine
Foo<int> f;
auto g = f; // fails!
}
Note: the destructor of NoCopy
is declared protected
to avoid virtual inheritance (Thanks for the hint, @Yakk).
回答2:
The method of deriving from a copyable or non-copyable base is the standard idiom for this type of problem (see also Stefan's comment). One way to implement it is like this:
template<bool> struct copyable
{
protected:
~copyable() = default;
};
template<> struct copyable<false>
{
copyable(copyable const&) = delete;
copyable&operator=(copyable const&) = delete;
protected:
~copyable() = default;
};
template<bool allow_copy>
class some_class : copyable<allow_copy> { /* ... */ };
回答3:
In C++20, we can use requires-clauses to constrain special member functions:
template <typename T>
class C {
public:
// ...
C(const C&) requires std::is_copy_constructible_v<T> // for example
{
// ...
}
C(C&&) requires std::is_move_constructible_v<T> // for example
{
// ...
}
// ...
};
A requires-clause does not make the function a function template, so these functions still qualify as special member functions and block the generated default special member functions. You can even have multiple, say, copy constructors, as long as they have different constraints.
来源:https://stackoverflow.com/questions/29430090/sfinae-away-a-copy-constructor