假设我正在编写一个C<T>
包含T
值的类模板,因此C<T>
只有在可复制的情况下才T
可以复制.通常,当模板可能支持或可能不支持某个操作时,您只需定义操作,并且由您的调用者决定是否在不安全时调用它:
template <typename T>
class C {
private:
T t;
public:
C(const C& rhs);
C(C&& rhs);
// other stuff
};
Run Code Online (Sandbox Code Playgroud)
但是,这会在复制构造函数的情况下产生问题,因为is_copy_constructible<C<T>>
即使T
不可复制也是如此; 如果被调用,则特征无法看到复制构造函数将是错误的.而且这是一个问题,因为,例如,vector
有时会避免使用移动构造函数,如果std::is_copy_constructible
是真实的.我怎样才能解决这个问题?
is_copy_constructible
如果构造函数显式或隐式默认,我相信会做正确的事情:
template <typename T>
class C {
private:
T t;
public:
C(const C& rhs) = default;
C(C&& rhs) = default;
// other stuff
};
Run Code Online (Sandbox Code Playgroud)
但是,并不总是可以构建您的类,以便默认的构造函数将做正确的事情.
我能看到的另一种方法是使用SFINAE来有条件地禁用复制构造函数:
template <typename T>
class C {
private:
T t;
public:
template <typename U …
Run Code Online (Sandbox Code Playgroud) 建议std::forward
通常仅限于完美转发函数模板参数的规范用例; 一些评论员甚至说这是唯一有效的用途std::forward
.但考虑这样的代码:
// Temporarily holds a value of type T, which may be a reference or ordinary
// copyable/movable value.
template <typename T>
class ValueHolder {
public:
ValueHolder(T value)
: value_(std::forward<T>(value)) {
}
T Release() {
T result = std::forward<T>(value_);
return result;
}
private:
~ValueHolder() {}
T value_;
};
Run Code Online (Sandbox Code Playgroud)
在这种情况下,不会出现完美转发的问题:由于这是一个类模板而不是一个函数模板,客户端代码必须明确指定T
,并可以选择是否以及如何对其进行ref-qualify.出于同样的原因,论证std::forward
不是"普遍参考".
尽管如此,这std::forward
似乎是一个很好的选择:我们不能把它排除在外,因为当它T
是一个只移动类型时它不起作用,我们不能使用std::move
它,因为当它T
是一个左值引用类型时它不起作用.当然,我们可以部分专门ValueHolder
使用直接初始化引用和std::move
值,但这似乎std::forward
是工作时的过度复杂.对于以下含义,这似乎也是一个合理的概念匹配std::forward
:我们试图通常转发可能会或可能不会引用的内容,只有我们将它转发给函数的调用者,而不是我们调用的函数.我们自己.
这是一个合理的用途std::forward …
std::unique_ptr<T,D>
指定存储不是T*
您可能期望的,但是类型的对象std::unique_ptr<T,D>::pointer
.D::pointer
如果存在这样的类型,T*
则基本上将其定义为,否则.因此,您可以通过适当地自定义删除器来自定义基础原始指针类型.
什么时候这样做是个好主意?它是干什么用的?我能够找到的唯一讨论就是这个注释,它暗示"在共享内存环境中更好地支持容器和智能指针",但这并没有完全解释.
我在玩 C++ 模板类型推导并设法编译了这个小程序。
template<typename T>
void f(const T& val)
{
val = 1;
}
int main()
{
int i = 0;
f<int&>(i);
}
Run Code Online (Sandbox Code Playgroud)
它可以在所有主要编译器上编译,但我不明白为什么。为什么可以f
赋值val
,什么时候val
显式标记const
?它是这些编译器中的错误还是根据 C++ 标准的有效行为?