使用 C++ 多年后,我意识到使用自定义类时语法中的一个怪癖。尽管是正确的语言行为,但它允许创建非常具有误导性的界面。
这里的例子:
class complex_arg {
double r_;
double phi_;
public:
std::complex<double> value() const {return r_*exp(phi_*std::complex<double>{0, 1});}
};
int main() {
complex_arg ca;
ca.value() = std::complex<double>(1000., 0.); // accepted by the compiler !?
assert( ca.value() != std::complex<double>(1000., 0.) ); // what !?
}
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/Y5Pcjsc8d
可以对类定义做什么来防止这种行为? (或者至少标记类的用户第三行并没有真正执行任何分配。)
我只看到一种出路,但它需要修改类,并且它不能很好地扩展(对于可以移动的大型类)。
const std::complex<double> value() const;
Run Code Online (Sandbox Code Playgroud)
我也尝试过[[nodiscard]] value()
,但没有帮助。
作为最后的手段,也许可以对返回的类型做一些事情std::complex<double>
?(也就是说,假设一个人控制着该班级)
请注意,我知道有时可能需要(优化)分配给新获得的值并将其传递给另一个函数f( ca.value() = bla )
。我并不是质疑这种用法本身(尽管它也很令人困惑);我的问题主要是ca.value() = bla;
作为一个独立的声明,它并不像它看起来的那样。
我有处理过的多边形数据.现在我想看看我的处理数据与原始数据的匹配程度.对于此任务,我想使用BOOST的多边形集合运算符.以下代码给了我一个段错误:
#include <iostream>
#include <boost/polygon/polygon.hpp>
using namespace boost::polygon::operators;
using namespace std;
typedef boost::polygon::polygon_data<double> BPolygon;
typedef boost::polygon::polygon_traits<BPolygon>::point_type BPoint;
typedef boost::polygon::polygon_set_data<double> BPolygonSet;
typedef std::vector<BPolygon> BPolygonVec;
double meassureError(BPolygonVec &polys1, BPolygonVec &polys2)
{
BPolygonSet set1;
BPolygonSet set2;
assign(set1, polys1);
assign(set2, polys2);
return area(set1 ^ set2);
}
int main(int argc, char *argv[])
{
BPolygonVec polys1;
BPolygonVec polys2;
loadPolysFromFile(polys1);
loadPolysFromFile(polys2);
cout << meassureError(polys1, polys2) << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
GDB输出:
Program received signal SIGSEGV, Segmentation fault.
0x08156ce7 in std::list<boost::polygon::point_data<double>, std::allocator<boost::polygon::point_data<double> > >::begin (this=0x0) at /usr/include/c++/4.8.2/bits/stl_list.h:759
759 …
Run Code Online (Sandbox Code Playgroud) Boost.ICL的interval_map
有两种行为:+=
和insert
。两者在不同的上下文中都是有用的。第一个将两个现有间隔的公共交点处的值相加。第二种方法仅在先前未分配的间隔中引入新值(在先前分配的间隔中保留值)。
但是,我需要一种与众不同的行为,这样,在下面的示例中,(1.,2.)->1 , (2.5,3.)->3, (3.,5.)->2
我得到了期望的,而不是得到了不需要的间隔图(1.,2.)->1 , (2.5,5.)->3
。
也就是说,新插入的值替换了旧值吗?我如何声明interval_map
要获得替换行为?
#include<boost/icl/interval_map.hpp>
int main(){
boost::icl::interval_map<double, int> joined_map;
joined_map.insert( std::make_pair(
boost::icl::interval<double>::open(1., 2.),
1
));
joined_map.insert( std::make_pair(
boost::icl::interval<double>::open(3., 5.),
2
));
joined_map.insert( std::make_pair(
boost::icl::interval<double>::open(2.5, 5.),
3
)); // this line doesn't replace the old value 2, it keeps it.
}
Run Code Online (Sandbox Code Playgroud)
奖金:那boost::icl::map
应该做什么?如何使用?
编辑1:这是使用C ++ 11的更明确和简化的示例代码
#include<boost/icl/interval_map.hpp>
#include<iostream>
namespace icl = boost::icl;
using interval = icl::interval<double>; …
Run Code Online (Sandbox Code Playgroud) 对于for-range循环语法,如何使原始指针的行为类似于范围.
double five = 5;
double* dptr = &five;
for(int& d : dptr) std::cout << d << std::endl;// will not execute if the pointer is null
Run Code Online (Sandbox Code Playgroud)
动机:
现在,vox populi可以将boost::optional
(未来std::optional
)值视为范围,因此可用于范围循环http://faithandbrave.hateblo.jp/entry/2015/01/29/173613.
当我重写我自己的简化版本时:
namespace boost {
template <class Optional>
decltype(auto) begin(Optional& opt) noexcept{
return opt?&*opt:nullptr;
}
template <class Optional>
decltype(auto) end(Optional& opt) noexcept{
return opt?std::next(&*opt):nullptr;
}
}
Run Code Online (Sandbox Code Playgroud)
用作
boost::optional<int> opt = 3;
for (int& x : opt) std::cout << x << std::endl;
Run Code Online (Sandbox Code Playgroud)
在查看代码时,我想象它也可以推广到原始(可空)指针.
double five = 5; …
Run Code Online (Sandbox Code Playgroud) 我想在进程之间使用共享内存。我尝试了 MPI_Win_allocate_shared 但当我执行程序时它给了我一个奇怪的错误:
文件./src/mpid/ch3/include/mpid_rma_shm.h
第 592 行断言失败:local_target_rank >= 0
internal ABORT
这是我的来源:
# include <stdlib.h>
# include <stdio.h>
# include <time.h>
# include "mpi.h"
int main ( int argc, char *argv[] );
void pt(int t[], int s);
int main ( int argc, char *argv[] )
{
int rank, size, shared_elem = 0, i;
MPI_Init ( &argc, &argv );
MPI_Comm_rank ( MPI_COMM_WORLD, &rank );
MPI_Comm_size ( MPI_COMM_WORLD, &size );
MPI_Win win;
int *shared;
if (rank == 0) shared_elem = …
Run Code Online (Sandbox Code Playgroud) 我有一个看起来或多或少像这样的功能:
template<class C> auto f(C const& c) -> decltype(begin(c)){
using std::begin;
return begin(c);
}
Run Code Online (Sandbox Code Playgroud)
函数的主体利用" using
和使用"成语和
感谢decltype
SFINAE,如果返回类型无效.
然而,它一般来说并不完美,因为我无法告诉它decltype
有using std
声明begin
.
template<class C> auto f(C const& c) -> decltype(std::begin(c))
Run Code Online (Sandbox Code Playgroud)
也会不一致,例如何时decltype(c)
和begin
属于不同的命名空间.
周围有路吗?
理想情况下,我想要类似的东西
template<class C> auto f(C const& c) -> decltype(using std::begin; begin(c))
Run Code Online (Sandbox Code Playgroud)
我认为lambda原则上可以工作
template<class C> auto f(C const& c) -> decltype([&]{using std::begin; return begin(c)})
Run Code Online (Sandbox Code Playgroud)
但是内部禁止使用lambdas decltype
.
在GCC中有一个有趣的语言扩展("表达式语句")是有希望的,但是它不能在函数体外工作(与未评估的上下文中不允许使用lambda). 否则它将是一个解决方案.
template<class C> auto g(C const& c)
->decltype(({using std::begin; begin(c);})){ …
Run Code Online (Sandbox Code Playgroud) c++ decltype using-declaration argument-dependent-lookup c++11
我有一段通用代码,在实例化时,归结为:
struct A{...};
A f1(){
A ret;
std::pair<A&, int> p(ret, 1);
p = g(); // g returns a pair<A, int>, but I am not interested in g
return ret; // RVO :)
};
Run Code Online (Sandbox Code Playgroud)
据我了解,这将适用于RVO.
问题是,这个其他代码是否会返回A
RVO 类型的对象?
A f2(){
std::pair<A, int> p;
p = g();
return p.first; // RVO ?
};
Run Code Online (Sandbox Code Playgroud)
我理解,由于返回对象被遮挡,它不会做RVO或者编译器可能无法选择"优化".但我没有看到为什么它不可能的根本原因,换句话说,我认为一致性,它应该做RVO(我想要).
我问的原因是因为我认为f2
代码比较优雅f1
.
这最后一个版本应该做RVO吗?如果是这样,它是编译器依赖的吗?
我用gcc 8.1原油测试给出了RVO不工作f2
和f3
以下:
struct A{
double something;
A() = default;
A(A const& other) : something(other.something){std::cerr << "cc" << …
Run Code Online (Sandbox Code Playgroud) 在容器中具有这些特征的原因是什么(https://en.cppreference.com/w/cpp/memory/allocator_traits)
propagate_on_container_copy_assignment Alloc::propagate_on_container_copy_assignment if present, otherwise std::false_type
propagate_on_container_move_assignment Alloc::propagate_on_container_move_assignment if present, otherwise std::false_type
propagate_on_container_swap Alloc::propagate_on_container_swap if present, otherwise std::false_type
Run Code Online (Sandbox Code Playgroud)
is_always_equal(since C++17) Alloc::is_always_equal if present, otherwise std::is_empty<Alloc>::type
Run Code Online (Sandbox Code Playgroud)
我知道容器实现在分配和交换的实现中会以一种或另一种方式表现。(并且处理这些情况是可怕的代码。)我也明白有时人们可能需要将移动容器保持在一种状态resizeble
或者至少可以调用一些最后的释放,因此分配器不能无效。(我个人认为这是一个弱论点。)
但问题是, 为什么这些信息不能成为自定义分配器类型本身的正常实现和语义的一部分?
我的意思是,容器复制分配可以尝试复制分配源分配器,如果语法复制分配没有真正复制,那么,就像说你的容器没有 propagate_on_container_copy_assignment
。
以同样的方式而不是使用 is_always_equal
一个实际上可以使分配器分配什么也不做。
(此外,如果is_always_equal
为真,则可以让operator==
分配器返回std::true_type
以发出信号。)
在我看来,这些特征似乎试图覆盖可以通过普通 C++ 方式提供给自定义分配器的语义。这似乎与泛型编程和当前的 C++ 哲学背道而驰。
唯一的原因,我认为这对于实现与“旧”容器的某种向后兼容性很有用。
如果我今天要编写一个新容器和/或一个新的非平凡分配器,我可以依靠分配器的语义而忘记这些特征吗?
在我看来,只要移动的分配器可以“解除分配”一个空指针状态(这意味着在这种特殊情况下主要是什么都不做),那么它应该没问题,如果resize
抛出,那也很好(有效) ,这只是意味着分配器无法再访问其堆。
编辑:实际上, 我可以这样简单地编写容器吗?并将复杂性委托给自定义分配器的语义?:
templata<class Allocator>
struct my_container{
Allocator alloc_;
...
my_container& operator=(my_container const& other){ …
Run Code Online (Sandbox Code Playgroud) allocator move-semantics copy-assignment c++11 move-assignment-operator
例如,假设我有一个函数交换,如下所示:
class A {
// all member variables and member functions are defined.
// Given that A deleted both copy constructor and
// overloaded assignment operator for some trivial reasons
A(const A&) = delete;
A& operator=(const A &) = delete;
};
template<typename T>
void swap (T &a, T &b) {
T temp;
temp = a;
a = b;
b = temp;
}
Run Code Online (Sandbox Code Playgroud)
现在我想swap()
处理除类型 A 的对象之外的所有类型,这是因为我有一些微不足道的原因,导致 A 没有实现重载赋值运算符或复制构造函数。
对于这种情况,我们可以编写一些断言或异常处理,以便当 swap()
函数用于类型 A 的对象时我们应该得到一个错误。
如果我们可以在编译时和运行时都做到这一点,请说明两者,这将帮助我了解更多。
C++ 有一个有用的功能,即模板参数隐含在模板类内的代码中A
。然而,对于建筑来说,这似乎与 CTAD 发生冲突。
如何让 CTAD 优先?
例如,在这里,成员中有一个错误f
,因为A
它被解释为A<T>
where T
is std::string
,而不是从参数 推导出来double
。
#include<string>
template<class T>
struct A {
A(T const& t) {}
auto f() {return A{5.0};} // error here triggered by line 11 `auto a2 = a1.f();`
};
int main() {
A a1{std::string{"hello"}};
auto a2 = a1.f();
}
Run Code Online (Sandbox Code Playgroud)