添加基于模板参数的拷贝构造函数
Add copy constructor based on template parameters
我有一个类似容器的 class,如果基础类型是只移动的,我希望它是只移动的,否则是可复制的。为简单起见,我们假设可复制性由单个 bool 模板参数决定:
template<bool IsCopyable>
struct Foo
{
Foo();
Foo(const Foo&); // only include this when IsCopyable is true
Foo(Foo&&);
Foo& operator=(const Foo&); // only when IsCopyable
Foo& operator=(Foo&&);
};
现在,我不能只使用 SFINAE 复制构造函数,因为这需要将其模板化,而模板化函数不能作为复制构造函数。另外,我不能只在复制构造函数中执行 static_assert()
。虽然这会捕获复制构造函数的错误使用,但它也使得 class 固有的复制可从外部构造(std::is_copy_constructible
类型特征将产生 true)。
顺便说一下,一个不幸的要求是它需要在 VC++ 2012 中编译,所以我不能使用花哨的表达式 SFINAE,继承 ctors,defaulted/deleted 函数或 constexpr if
(尽管如此,如果您对 C++17 有一个巧妙的解决方案,我仍然很想听听它:))
显而易见的方法是使用模板特化。我宁愿不走这条路,因为实际上 Foo 有很多功能,我不想重复自己。尽管如此,这似乎是我唯一的选择,我可以使用基数 class 实现一些代码共享,如下所示:
// Base functionality
template<bool IsCopyable>
struct FooBase
{
FooBase();
// move ctor and assignment can go here
FooBase(FooBase&&);
FooBase& operator=(FooBase&&);
// some generic conversion ctor and assignment that I happen to need
template<class T> FooBase(T&& t);
template<class T> FooBase& operator=(T&&);
// ... all sorts of functionality and datamembers
};
// Foo<false>
template<bool IsCopyable>
struct Foo : FooBase<IsCopyable>
{
// can't use inheriting ctors in VS 2012, wrap the calls manually:
Foo() { }
Foo(Foo&& other) : FooBase<IsCopyable>(std::move(other)) { }
Foo& operator=(Foo&& other)
{
FooBase<IsCopyable>::operator=(std::move(other));
return *this;
}
template<class T> Foo(T&& t) : FooBase<IsCopyable>(std::forward<T>(t)) { }
template<class T> Foo& operator=(T&& t)
{
FooBase<IsCopyable>::operator=(std::forward<T>(t));
return *this;
}
};
// Foo<true>
template<>
struct Foo<true> : FooBase<true>
{
// add these
Foo(const Foo&);
Foo& operator=(const Foo&);
// wrapping calls because of VS 2012:
Foo() { }
Foo(Foo&& other) : FooBase<true>(std::move(other)) { }
Foo& operator=(Foo&& other)
{
FooBase<true>::operator=(std::move(other));
return *this;
}
template<class T> Foo(T&& t) : FooBase<true>(std::forward<T>(t)) { }
template<class T> Foo& operator=(T&& t)
{
FooBase<true>::operator=(std::forward<T>(t));
return *this;
}
};
还是有点啰嗦。幸运的是,一旦您可以使用继承构造函数和默认函数,它就会变得更清晰。不过,我希望有一种更简单的方法,最好是不使用基数 class.
struct nonesuch {
private:
~nonesuch();
nonesuch(const nonesuch&);
void operator=(const nonesuch&);
};
template<bool IsCopyable>
struct Foo {
Foo(const typename std::conditional<IsCopyable, Foo, nonesuch>::type& other) {
// copy ctor impl here
}
private:
Foo(const typename std::conditional<!IsCopyable, Foo, nonesuch>::type&);
};
同样适用于作业。
我不知道是否可以使用您的 VC++2012 但是...如果我没记错的话,我认为有一种方法比继承自 FooBase
更好一些.
一种自动继承。
您可以声明结构
template <bool>
struct Bar;
并专门化 true
版本所有 constructor/operators
template <>
struct Bar<true>
{
Bar ()
{ std::cout << "Bar ()" << std::endl; };
Bar (Bar &&)
{ std::cout << "Bar (Bar &&)" << std::endl; }
Bar (Bar const &)
{ std::cout << "Bar (Bar const &)" << std::endl; }
Bar & operator= (Bar &&)
{ std::cout << "operator= (Bar &&)" << std::endl; return *this; }
Bar & operator= (Bar const &)
{ std::cout << "operator= (Bar const &)" << std::endl; return *this; }
};
现在您可以专门化继承自 Bar<true>
的 false
版本,确认 (= default
) 您想要的并删除 (= delete
) 您不需要的想;像
template <>
struct Bar<false> : public Bar<true>
{
using Bar<true>::Bar;
// confirmed constructor/operators
Bar () = default;
Bar (Bar &&) = default;
Bar & operator= (Bar &&) = default;
// deleted constructor/operators
Bar (Bar const &) = delete;
Bar & operator= (Bar const &) = delete;
};
以下是完整的工作示例
#include <iostream>
template <bool>
struct Bar;
template <>
struct Bar<true>
{
Bar ()
{ std::cout << "Bar ()" << std::endl; };
Bar (Bar &&)
{ std::cout << "Bar (Bar &&)" << std::endl; }
Bar (Bar const &)
{ std::cout << "Bar (Bar const &)" << std::endl; }
Bar & operator= (Bar &&)
{ std::cout << "operator= (Bar &&)" << std::endl; return *this; }
Bar & operator= (Bar const &)
{ std::cout << "operator= (Bar const &)" << std::endl; return *this; }
};
template <>
struct Bar<false> : public Bar<true>
{
using Bar<true>::Bar;
// confirmed constructor/operators
Bar () = default;
Bar (Bar &&) = default;
Bar & operator= (Bar &&) = default;
// deleted constructor/operators
Bar (Bar const &) = delete;
Bar & operator= (Bar const &) = delete;
};
int main ()
{
Bar<true> bt1;
Bar<false> bf1;
auto bt2 = bt1;
// auto bf2 = bf1; compilation error (copy constructor deleted)
auto bt3 = std::move(bt1);
auto bf3 = std::move(bf1);
Bar<true> bt4;
Bar<false> bf4;
bt4 = bt3;
// bf4 = bf3; compilation error (operator= (Bar const &) deleted)
bt3 = std::move(bt4);
bf3 = std::move(bf4);
}
您不能使用基数 class 来有选择地禁用复制吗?这样你就不需要重复主要 class:
的任何其他功能
template <bool b>
struct MaybeCopyable {};
template <>
struct MaybeCopyable<false> {
MaybeCopyable(const MaybeCopyable&) = delete;
MaybeCopyable& operator=(const MaybeCopyable&) = delete;
};
template<bool IsCopyable>
struct Foo : MaybeCopyable<IsCopyable> {
// other functionality
};
如果MaybeCopyable<false>
是基class之一,Foo
的复制构造函数将被自动删除。
我有一个类似容器的 class,如果基础类型是只移动的,我希望它是只移动的,否则是可复制的。为简单起见,我们假设可复制性由单个 bool 模板参数决定:
template<bool IsCopyable>
struct Foo
{
Foo();
Foo(const Foo&); // only include this when IsCopyable is true
Foo(Foo&&);
Foo& operator=(const Foo&); // only when IsCopyable
Foo& operator=(Foo&&);
};
现在,我不能只使用 SFINAE 复制构造函数,因为这需要将其模板化,而模板化函数不能作为复制构造函数。另外,我不能只在复制构造函数中执行 static_assert()
。虽然这会捕获复制构造函数的错误使用,但它也使得 class 固有的复制可从外部构造(std::is_copy_constructible
类型特征将产生 true)。
顺便说一下,一个不幸的要求是它需要在 VC++ 2012 中编译,所以我不能使用花哨的表达式 SFINAE,继承 ctors,defaulted/deleted 函数或 constexpr if
(尽管如此,如果您对 C++17 有一个巧妙的解决方案,我仍然很想听听它:))
显而易见的方法是使用模板特化。我宁愿不走这条路,因为实际上 Foo 有很多功能,我不想重复自己。尽管如此,这似乎是我唯一的选择,我可以使用基数 class 实现一些代码共享,如下所示:
// Base functionality
template<bool IsCopyable>
struct FooBase
{
FooBase();
// move ctor and assignment can go here
FooBase(FooBase&&);
FooBase& operator=(FooBase&&);
// some generic conversion ctor and assignment that I happen to need
template<class T> FooBase(T&& t);
template<class T> FooBase& operator=(T&&);
// ... all sorts of functionality and datamembers
};
// Foo<false>
template<bool IsCopyable>
struct Foo : FooBase<IsCopyable>
{
// can't use inheriting ctors in VS 2012, wrap the calls manually:
Foo() { }
Foo(Foo&& other) : FooBase<IsCopyable>(std::move(other)) { }
Foo& operator=(Foo&& other)
{
FooBase<IsCopyable>::operator=(std::move(other));
return *this;
}
template<class T> Foo(T&& t) : FooBase<IsCopyable>(std::forward<T>(t)) { }
template<class T> Foo& operator=(T&& t)
{
FooBase<IsCopyable>::operator=(std::forward<T>(t));
return *this;
}
};
// Foo<true>
template<>
struct Foo<true> : FooBase<true>
{
// add these
Foo(const Foo&);
Foo& operator=(const Foo&);
// wrapping calls because of VS 2012:
Foo() { }
Foo(Foo&& other) : FooBase<true>(std::move(other)) { }
Foo& operator=(Foo&& other)
{
FooBase<true>::operator=(std::move(other));
return *this;
}
template<class T> Foo(T&& t) : FooBase<true>(std::forward<T>(t)) { }
template<class T> Foo& operator=(T&& t)
{
FooBase<true>::operator=(std::forward<T>(t));
return *this;
}
};
还是有点啰嗦。幸运的是,一旦您可以使用继承构造函数和默认函数,它就会变得更清晰。不过,我希望有一种更简单的方法,最好是不使用基数 class.
struct nonesuch {
private:
~nonesuch();
nonesuch(const nonesuch&);
void operator=(const nonesuch&);
};
template<bool IsCopyable>
struct Foo {
Foo(const typename std::conditional<IsCopyable, Foo, nonesuch>::type& other) {
// copy ctor impl here
}
private:
Foo(const typename std::conditional<!IsCopyable, Foo, nonesuch>::type&);
};
同样适用于作业。
我不知道是否可以使用您的 VC++2012 但是...如果我没记错的话,我认为有一种方法比继承自 FooBase
更好一些.
一种自动继承。
您可以声明结构
template <bool>
struct Bar;
并专门化 true
版本所有 constructor/operators
template <>
struct Bar<true>
{
Bar ()
{ std::cout << "Bar ()" << std::endl; };
Bar (Bar &&)
{ std::cout << "Bar (Bar &&)" << std::endl; }
Bar (Bar const &)
{ std::cout << "Bar (Bar const &)" << std::endl; }
Bar & operator= (Bar &&)
{ std::cout << "operator= (Bar &&)" << std::endl; return *this; }
Bar & operator= (Bar const &)
{ std::cout << "operator= (Bar const &)" << std::endl; return *this; }
};
现在您可以专门化继承自 Bar<true>
的 false
版本,确认 (= default
) 您想要的并删除 (= delete
) 您不需要的想;像
template <>
struct Bar<false> : public Bar<true>
{
using Bar<true>::Bar;
// confirmed constructor/operators
Bar () = default;
Bar (Bar &&) = default;
Bar & operator= (Bar &&) = default;
// deleted constructor/operators
Bar (Bar const &) = delete;
Bar & operator= (Bar const &) = delete;
};
以下是完整的工作示例
#include <iostream>
template <bool>
struct Bar;
template <>
struct Bar<true>
{
Bar ()
{ std::cout << "Bar ()" << std::endl; };
Bar (Bar &&)
{ std::cout << "Bar (Bar &&)" << std::endl; }
Bar (Bar const &)
{ std::cout << "Bar (Bar const &)" << std::endl; }
Bar & operator= (Bar &&)
{ std::cout << "operator= (Bar &&)" << std::endl; return *this; }
Bar & operator= (Bar const &)
{ std::cout << "operator= (Bar const &)" << std::endl; return *this; }
};
template <>
struct Bar<false> : public Bar<true>
{
using Bar<true>::Bar;
// confirmed constructor/operators
Bar () = default;
Bar (Bar &&) = default;
Bar & operator= (Bar &&) = default;
// deleted constructor/operators
Bar (Bar const &) = delete;
Bar & operator= (Bar const &) = delete;
};
int main ()
{
Bar<true> bt1;
Bar<false> bf1;
auto bt2 = bt1;
// auto bf2 = bf1; compilation error (copy constructor deleted)
auto bt3 = std::move(bt1);
auto bf3 = std::move(bf1);
Bar<true> bt4;
Bar<false> bf4;
bt4 = bt3;
// bf4 = bf3; compilation error (operator= (Bar const &) deleted)
bt3 = std::move(bt4);
bf3 = std::move(bf4);
}
您不能使用基数 class 来有选择地禁用复制吗?这样你就不需要重复主要 class:
的任何其他功能template <bool b>
struct MaybeCopyable {};
template <>
struct MaybeCopyable<false> {
MaybeCopyable(const MaybeCopyable&) = delete;
MaybeCopyable& operator=(const MaybeCopyable&) = delete;
};
template<bool IsCopyable>
struct Foo : MaybeCopyable<IsCopyable> {
// other functionality
};
如果MaybeCopyable<false>
是基class之一,Foo
的复制构造函数将被自动删除。