计算常量表达式以初始化时constexpr,可以抛出异常.例如,这里是一个示例,其中一个常量表达式的计算被防止溢出:
#include <iostream>
#include <stdexcept>
constexpr int g(int n, int n0, int n1) {
return n == 0? n1: g(n - 1, n1, n0 + n1);
}
constexpr int f(int n) {
return n < 42? g(n, 0, 1): throw std::out_of_range("too big");
}
int main()
{
try {
constexpr int f41 = f(41); // OK: constexpr
int f43 = f(43); // OK: throws an exception
constexpr int f42 = f(42); // not OK but what happens?
}
catch …Run Code Online (Sandbox Code Playgroud) 对于类类型,可以分配实际上不允许内置类型的临时对象.此外,默认生成的赋值运算符甚至会产生左值:
int() = int(); // illegal: "expression is not assignable"
struct B {};
B& b = B() = B(); // compiles OK: yields an lvalue! ... but is wrong! (see below)
Run Code Online (Sandbox Code Playgroud)
对于最后一个语句,赋值运算符的结果实际上用于初始化非const引用,该引用将在语句之后立即变为陈旧:引用未直接绑定到临时对象(它不能作为临时对象只能是绑定到一个const或右值引用)但绑定到其生命周期未延长的赋值结果.
另一个问题是从赋值运算符返回的左值看起来不像它可以被移动,尽管它实际上是指临时的.如果有任何东西使用赋值的结果来获取值,它将被复制而不是移动,尽管移动是完全可行的.此时值得注意的是,问题是根据赋值运算符描述的,因为此运算符通常可用于值类型并返回左值引用.任何返回对象引用的函数都存在同样的问题,即*this.
一个潜在的解决方法是重载赋值运算符(或返回对象引用的其他函数)以考虑对象的类型,例如:
class G {
public:
// other members
G& operator=(G) & { /*...*/ return *this; }
G operator=(G) && { /*...*/ return std::move(*this); }
};
Run Code Online (Sandbox Code Playgroud)
C++ 11提供了如上所述重载赋值运算符的可能性,并且可以防止上面提到的细微对象失效并同时允许将赋值结果移动到临时值.这两个运营商的实施可能完全相同.虽然实现可能相当简单(基本上只是swap()两个对象中的一个),但它仍然意味着提出问题的额外工作:
返回对象的引用的函数(例如,赋值运算符)是否应该观察被赋值对象的右值?
另外一个(在评论中由Simple提到)是不重载赋值运算符,但是使用a &来限制它的使用到lvalues:
class GG {
public:
// other …Run Code Online (Sandbox Code Playgroud) 由于设计std::ostream无法移动,因此问题变为:如何std::ostream移动以便可以写入不同的目的地?
基本目标是让一个工厂函数获取一个URI并返回一些东西,让我们调用它,omstream(输出可移动流)可以像下面这样使用std::ostream:
omstream stream_factory(std::string const& uri);
void process(std::ostream& out);
int main(int ac, char* av[]) {
omstream destination{ stream_factory(ac == 2? av[1]: "example.txt") };
process(destination);
}
Run Code Online (Sandbox Code Playgroud)
该omstream会负责妥善移动对象:
class omstream
: public std::ostream {
// suitable members
public:
omstream(/* suitable constructor arguments */);
omstream(omstream&& other) // follow recipe of 27.9.1.11 [ofstream.cons] paragraph 4
: std:ios(std::move(other))
, std::ostream(std::move(other))
// move any members {
this->set_rdbuf(/* get the stream buffer */);
}
// other …Run Code Online (Sandbox Code Playgroud) 何时constexpr相对于constexpr具有静态存储持续时间的非非本地对象构造对象?他们是否在初始化任何其他对象之前开始他们的生活,即在动态初始化之前?
我正在考虑使用一个string_literal类(实例)是否合理,例如,将std::strings与某些关键字进行比较:
class string_literal
{
// private members
public:
constexpr string_literal(char const* b);
bool operator== (std::string const& other) const;
bool operator!= (std::string const& other) const;
// other member functions
};
constexpr string_literal hello("hello");
void f(std::string const& s) {
if (s == hello) {
// do something
}
}
Run Code Online (Sandbox Code Playgroud)
由于string_literal可以在编译时解析字符串文字来定位第一个空字符,我可以想象这些比较可以比比较std::string字符串文字更快.但是,为了安全起见,hello在静态初始化期间在运行时执行第一个构造函数时,必须容易构造对象:否则,当它们尚未构造时,可能会意外地访问这些对象.
我的问题的简短版本是:我如何使用std::bind()标准库算法之类的东西?
由于短版本有点缺乏细节,这里有一点解释:假设我有算法std::transform(),现在我想实现std::copy()(是的,我意识到std::copy()标准C++库中有).由于我很懒惰,我显然想要使用现有的实现std::transform().当然,我可以这样做:
struct identity {
template <typename T>
auto operator()(T&& value) const -> T&& { return std::forward<T>(value); }
};
template <typename InIt, typename OutIt>
auto copy(InIt begin, InIt end, OutIt to) -> OutIt {
return std::transform(begin, end, to, identity());
}
Run Code Online (Sandbox Code Playgroud)
不知何故,这种实现有点像一种算法的配置.例如,似乎std::bind()应该能够完成这项工作,但只是使用std::bind()不起作用:
namespace P = std::placeholders;
auto copy = std::bind(std::transform, P::_1, P::_2, P::_3, identity());
Run Code Online (Sandbox Code Playgroud)
问题是编译器无法仅从算法中确定适当的模板参数,如果存在&或不存在则无关紧要.有没有什么可以像使用std::bind()工作一样的方法?由于这是期待,我很满意一个解决方案,它已经提出了包含在C++标准中的任何内容.另外,为了摆脱我的懒惰,我很乐意在前面做一些工作,以便以后更容易使用.以这种方式思考:在我作为库实现者的角色中,我会把事情放在一起,这样每个库用户都可以变得懒惰:我是一个忙碌的实现者,但是一个懒惰的用户.
如果您想要一个现成的试验台:这是一个完整的程序.
#include …Run Code Online (Sandbox Code Playgroud) 当处理某些数据流(例如,来自网络的请求)时,使用一些临时存储器是很常见的.例如,URL可以分成多个字符串,每个字符串可能从堆中分配内存.这些实体的使用通常是短暂的,并且内存总量通常相对较小,应该适合CPU缓存.
在用于临时字符串的内存被释放时,字符串内容很可能只存在于缓存中.但是,CPU并不知道要释放的内存:释放只是内存管理系统中的更新.结果,当CPU高速缓存用于其他存储器时,CPU可能最终不必要地将未使用的内容写入实际存储器 - 除非存储器释放以某种方式向CPU指示存储器不再被使用.因此,问题变为:
释放内存的内存管理功能是否表示可以丢弃相应内存的内容?有没有办法向CPU指示不再使用内存?(至少,对于某些CPU:显然,架构之间可能存在差异)由于不同的实现可能会在质量上有所不同,可能会或可能不会做任何花哨的事情,问题实际上是否有任何内存管理实现将内存指示为未使用?
我确实意识到始终使用相同的内存领域可能是一种缓解策略,以避免对实际内存的不必要写入.在这种情况下,将使用相同的缓存内存.类似地,内存分配可能总是产生相同的内存,也避免了不必要的内存传输.但是,我可能不需要依赖任何适用的技术.
memory performance memory-management dynamic-memory-allocation cpu-cache
我需要创建一个这样的模板函数:
template<typename T>
void foo(T a)
{
if (T is a subclass of class Bar)
do this
else
do something else
}
Run Code Online (Sandbox Code Playgroud)
我也可以想象使用模板特化来做它...但我从未见过超类的所有子类的模板特化.我不想为每个子类重复专门化代码
关于复制和交换习语有几个很好的答案,例如,解释复制和交换习语以及解释移动语义.适用于复制和移动分配的基本习惯用法如下所示:
T& T::operator=(T other) {
this->swap(other);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
此赋值适用于复制和移动赋值,因为other复制或移动构造取决于赋值的右侧是左值还是右值.
现在让我们让有状态的分配器进入图片:如果T在分配器类型上参数化,例如,std::vector<S, A>上面的成语并不总是有效!具体来说,std::allocator_traits<A>包含三种类型,指示是否应传播分配器:
std::allocator_traits<A>::propagate_on_container_copy_assignmentstd::allocator_traits<A>::propagate_on_container_move_assignmentstd::allocator_traits<A>::propagate_on_container_swap这三个特征的默认值是std::false_type(见20.6.8.1 [allocator.traits.types]第7,8和9段).如果任何这些特征是正常的复制和交换习惯用法,std::false_type 并且分配器是有状态的,有可能比较不等.对于复制分配,修复非常简单:
T& T::operator= (T const& other) {
T(other, this->get_allocator()).same_allocator_swap(*this);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
也就是说,首先复制对象,提供LHS的分配器对象,然后使用一个函数交换成员,如果两个对象都使用相同的分配器,即何时使用other.get_allocator() == this->get_allocator().
移动分配时,如果可以移动RHS,则不要复制RHS.如果分配器相同,则可以移动RHS.否则,需要使用适当的分配器复制对象,从而导致这样的赋值运算符
T& T::operator= (T&& other) {
T(std::move(other), this->get_allocator()).same_allocator_swap(*this);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
这里的方法是将构造临时移动,同时传递分配器.这样做假定类型T确实有一个"移动构造函数",同时T&&为对象状态和分配器指定要使用的分配器.移动构造函数的负担是根据分配器的不同或相同而复制或移动.
由于第一个参数的传递方式不同,因此复制和移动赋值不能折叠到赋值运算符的一个版本中.因此,他们需要将他们的参数作为参考,并且需要明确地复制或移动抑制复制省略的可能性的论据.
在涉及分配器时,是否有更好的方法来处理赋值运算符?
为用户定义的类型创建格式化输出时,通常需要定义自定义格式标记.例如,如果自定义字符串类可以选择在字符串周围添加引号,那将是很好的:
String str("example");
std::cout << str << ' ' << squotes << str << << ' ' << dquotes << str << '\n';
Run Code Online (Sandbox Code Playgroud)
应该产生
example 'example' "example"
Run Code Online (Sandbox Code Playgroud)
很容易创建操纵器来更改格式化标志本身:
std::ostream& squotes(std::ostream& out) {
// what magic goes here?
return out;
}
std::ostream& dquotes(std::ostream& out) {
// similar magic as above
return out;
}
std::ostream& operator<< (std::ostream& out, String const& str) {
char quote = ????;
return quote? out << quote << str.c_str() << quote: str.c_str();
}
Run Code Online (Sandbox Code Playgroud)
...但是操纵器如何存储哪些引号应该与流一起使用,然后让输出操作符检索值?
我有一个模板,它采用类型的非类型模板参数unsigned long long.实例化这个模板很快就会变得混乱,因为它涉及很多数字.事实上,理想的我会用二进制表示使事情变得更糟:有多达64个0和1字符.如何创建视觉上可识别的参数.例如:
template <unsigned long long>
struct use_mask {
// for this question it doesn't matter what the template actually does
};
int main() {
use_mask<0b1001001010010010> unreadable; // OK, there are no binary literals
use_mask<0b1001,0010,1001,0010> more_readable; // ... nor are there digit separators
}
Run Code Online (Sandbox Code Playgroud)
有没有办法近似后一种符号,可能在值之前和之后有一些东西?