问题描述
|
考虑这种情况:
template<typename T>
struct A {
A(A ???&) = default;
A(A&&) { /* ... */ }
T t;
};
我显式声明了一个move构造函数,因此,如果我想拥有一个未删除的复制构造函数,则需要显式声明一个复制构造函数。如果要对它进行“ 1”设置,如何找到正确的参数类型?
A(A const&) = default; // or
A(A &) = default; // ?
我也对您是否遇到过这样的情况真正在实际程序中弹出的情况感兴趣。规格说
明确默认的功能应...
具有相同的声明函数类型(除了可能使引用限定符不同以及在
如果是复制构造函数或复制赋值运算符,则参数类型可以是\“对非常量T的引用”,其中T是成员函数的类的名称),就好像它是隐式声明的一样,
如果隐式声明的副本构造函数的类型为A &
,我想让我的副本构造函数的参数类型为A &
显式默认。但是,如果隐式声明的副本构造函数的参数类型为A const&
,则我不想让我的默认默认副本构造函数的参数类型为A &
,因为这将禁止从const左值进行复制。
我不能同时声明两个版本,因为在隐式声明的函数具有参数类型“ 3”而我的显式默认声明具有参数类型“ 5”的情况下,这将违反上述规则。据我所知,仅当隐式声明为A const&
且显式声明为A &
时才允许有区别。
编辑:事实上,规范说,甚至
如果某个函数的第一个十进制显式默认为
感叹,...
对于复制构造函数,移动构造函数,复制赋值运算符或移动赋值运算符,其参数类型应与隐式声明的参数类型相同。
因此,我需要定义这些类之外的类(我认为这没有什么坏处,因为据我所知,唯一的区别是该函数将变得不平凡,无论如何在这些情况下都是如此)
template<typename T>
struct A {
A(A &);
A(A const&);
A(A&&) { /* ... */ }
T t;
};
// valid!?
template<typename T> A<T>::A(A const&) = default;
template<typename T> A<T>::A(A &) = default;
好吧,我发现如果显式声明的函数为A const&
,则无效,而隐式声明为A &
:
由用户提供的显式默认函数(即在其首次声明后显式默认)在显式默认的位置定义;如果将此类函数隐式定义为Delete,则程序格式错误。
这与GCC所做的匹配。现在,我如何才能实现与隐式声明的构造函数的类型匹配的最初目标?
解决方法
在我看来,您将需要某种类型的演绎(概念?)。
除非成员之一要求将其写为“ 15”,否则编译器通常将使用“ 14”版本。因此,我们可以包装一些小模板黑客,以检查每个成员具有哪个版本的副本构造函数。
最新
请在ideone上进行咨询,或在代码段之后按Clang阅读错误。
#include <memory>
#include <type_traits>
template <bool Value,typename C>
struct CopyConstructorImpl { typedef C const& type; };
template <typename C>
struct CopyConstructorImpl<false,C> { typedef C& type; };
template <typename C,typename T>
struct CopyConstructor {
typedef typename CopyConstructorImpl<std::is_constructible<T,T const&>::value,C>::type type;
};
// Usage
template <typename T>
struct Copyable {
typedef typename CopyConstructor<Copyable<T>,T>::type CopyType;
Copyable(): t() {}
Copyable(CopyType) = default;
T t;
};
int main() {
{
typedef Copyable<std::auto_ptr<int>> C;
C a; C const b;
C c(a); (void)c;
C d(b); (void)d; // 32
}
{
typedef Copyable<int> C;
C a; C const b;
C c(a); (void)c;
C d(b); (void)d;
}
}
这使:
6167745.cpp:32:11: error: no matching constructor for initialization of \'C\' (aka \'Copyable<std::auto_ptr<int> >\')
C d(b); (void)d;
^ ~
6167745.cpp:22:7: note: candidate constructor not viable: 1st argument (\'const C\' (aka \'const Copyable<std::auto_ptr<int> >\')) would lose const qualifier
Copyable(CopyType) = default;
^
6167745.cpp:20:7: note: candidate constructor not viable: requires 0 arguments,but 1 was provided
Copyable(): t() {}
^
1 error generated.
版前
这是我能想到的最好的方法:
#include <memory>
#include <type_traits>
// Usage
template <typename T>
struct Copyable
{
static bool constexpr CopyByConstRef = std::is_constructible<T,T const&>::value;
static bool constexpr CopyByRef = !CopyByConstRef && std::is_constructible<T,T&>::value;
Copyable(): t() {}
Copyable(Copyable& rhs,typename std::enable_if<CopyByRef>::type* = 0): t(rhs.t) {}
Copyable(Copyable const& rhs,typename std::enable_if<CopyByConstRef>::type* = 0): t(rhs.t) {}
T t;
};
int main() {
{
typedef Copyable<std::auto_ptr<int>> C; // 21
C a; C const b; // 22
C c(a); (void)c; // 23
C d(b); (void)d; // 24
}
{
typedef Copyable<int> C; // 27
C a; C const b; // 28
C c(a); (void)c; // 29
C d(b); (void)d; // 30
}
}
几乎可以正常工作...除了在构建“ a”时出现一些错误。
6167745.cpp:14:78: error: no type named \'type\' in \'std::enable_if<false,void>\'
Copyable(Copyable const& rhs,typename std::enable_if<CopyByConstRef>::type* = 0): t(rhs.t) {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
6167745.cpp:22:11: note: in instantiation of template class \'Copyable<std::auto_ptr<int> >\' requested here
C a; C const b;
^
和:
6167745.cpp:13:67: error: no type named \'type\' in \'std::enable_if<false,void>\'
Copyable(Copyable& rhs,typename std::enable_if<CopyByRef>::type* = 0): t(rhs.t) {}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
6167745.cpp:28:11: note: in instantiation of template class \'Copyable<int>\' requested here
C a; C const b;
^
两者都是由于相同的原因发生的,我不明白为什么。似乎即使我有默认构造函数,编译器仍会尝试实现所有构造函数。我本以为SFINAE将适用,但似乎并非如此。
但是,正确检测到错误行24:
6167745.cpp:24:11: error: no matching constructor for initialization of \'C\' (aka \'Copyable<std::auto_ptr<int> >\')
C d(b); (void)d;
^ ~
6167745.cpp:13:7: note: candidate constructor not viable: 1st argument (\'const C\' (aka \'const Copyable<std::auto_ptr<int> >\')) would lose const qualifier
Copyable(Copyable& rhs,typename std::enable_if<CopyByRef>::type* = 0): t(rhs.t) {}
^
6167745.cpp:11:7: note: candidate constructor not viable: requires 0 arguments,but 1 was provided
Copyable(): t() {}
^
在这里,我们可以看到CopyByConstRef已从重载集中正确退出,这要归功于SFINAE。
, 我想我看不到问题了……与实现复制构造函数的常见情况有什么不同?
如果您的副本构造函数不会修改参数(并且隐式定义的副本构造函数不会这样做),则应将参数作为常量引用传递。我所知道的唯一一个不使用常量引用作为参数的副本构造函数的用例是,在C ++ 03中,当您想要实现移动lastd::auto_ptr
时,这通常是个坏主意。在C ++ 0x中,移动将像使用move构造函数一样实现。
, 我从未见过隐式副本构造函数为A&
的情况-在任何情况下都应以you24ѭ做为好。