Home Php C# Sql C C++ Javascript Python Java Go Android Git Linux Asp.net Django .net Node.js Ios Xcode Cocoa Iphone Mysql Tomcat Mongodb Bash Objective-c Scala Visual-studio Apache Elasticsearch Jar Eclipse Jquery Ruby-on-rails Ruby Rubygems Android-studio Spring Lua Sqlite Emacs Ubuntu Perl Docker Swift Amazon-web-services Svn Html Ajax Xml Java-ee Maven Intellij-idea Rvm Macos Unix Css Ipad Postgresql Css3 Json Windows-server Vue.js Typescript Oracle Hibernate Internet-explorer Github Tensorflow Laravel Symfony Redis Html5 Google-app-engine Nginx Firefox Sqlalchemy Lucene Erlang Flask Vim Solr Webview Facebook Zend-framework Virtualenv Nosql Ide Twitter Safari Flutter Bundle Phonegap Centos Sphinx Actionscript Tornado Register | Login | Edit Tags | New Questions | 繁体 | 简体


10 questions online user: 5

8
votes
answers
32 views
+10

在C ++头文件中使用指令的位置

对于我的项目,我使用了一些非常复杂的数据结构,例如

  std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>>   

为了便于阅读,我想声明类型别名。我构建项目的代码已经通过在头文件中全局放置使用语句来实现这一点:

  / / bar.h #ifndef BAR_H #define BAR_H #include< unordered_map> #include< list> #include< memory> #include“foo.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; class Bar {FooTable create_foo(); }; #endif   

由于我的C ++知识有点生疏,我只是采用了这种风格 - 但现在我读到使用使用以这种方式可能会有问题,因为它强制包含这个标题的所有内容。

尽管有大量的谷歌搜索,我找不到如何正确处理这个问题的具体答案,只有很多关于不该做的事情的陈述。所以,我只是把使用放在类中:

  // bar.h #ifndef BAR_H #define BAR_H #include< unordered_map> #include< list> #include< memory> #include“foo.h”class Bar {using FooTable = std :: unordered_map< int,std :: list< std::shared_ptr< const Foo>>&gt ;; FooTable create_foo(); }; #endif   

然而,这有一个缺点,我需要在源文件中重述别名:

  // bar.cpp #include“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; FooTable Bar :: create_foo(){...}   

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? shared_ptr< const Foo>>&gt ;; FooTable create_foo(); }; #endif

然而,这有一个缺点,我需要在源文件中重述别名:

  // bar.cpp #include“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; FooTable Bar :: create_foo(){...}   

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? shared_ptr< const Foo>>&gt ;; FooTable create_foo(); }; #endif

然而,这有一个缺点,我需要在源文件中重述别名:

  // bar.cpp #include“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; FooTable Bar :: create_foo(){...}   

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? const Foo>>&gt ;; FooTable create_foo(); }; #endif

然而,这有一个缺点,我需要在源文件中重述别名:

  // bar.cpp #include“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; FooTable Bar :: create_foo(){...}   

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? const Foo>>&gt ;; FooTable create_foo(); }; #endif

然而,这有一个缺点,我需要在源文件中重述别名:

  // bar.cpp #include“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; FooTable Bar :: create_foo(){...}   

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? FooTable create_foo(); }; #endif

然而,这有一个缺点,我需要在源文件中重述别名:

  // bar.cpp #include“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; FooTable Bar :: create_foo(){...}   

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? FooTable create_foo(); }; #endif

然而,这有一个缺点,我需要在源文件中重述别名:

  // bar.cpp #include“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; FooTable Bar :: create_foo(){...}   

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? / pre>

但是这有一个缺点,我需要在源文件中重述别名:

  // bar.cpp#包括“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; FooTable Bar :: create_foo(){...}   

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? / pre>

但是这有一个缺点,我需要在源文件中重述别名:

  // bar.cpp#包括“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo>>&gt ;; FooTable Bar :: create_foo(){...}   

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? pre class =“lang-cpp prettyprint-override”> // bar.cpp #include“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo> >取代; FooTable Bar :: create_foo(){...}

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? pre class =“lang-cpp prettyprint-override”> // bar.cpp #include“bar.h”使用FooTable = std :: unordered_map< int,std :: list< std :: shared_ptr< const Foo> >取代; FooTable Bar :: create_foo(){...}

虽然这似乎有用,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? create_foo(){...}

虽然这似乎有效,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名? create_foo(){...}

虽然这似乎有效,但我不确定这是否安全......我的直觉告诉我它有点难看。所以在我重写整个项目之前,我想我会问:是否有更好/更优雅/更安全的方法来做到这一点?或者我应该完全避免在头文件中使用类型别名?

沙发
+50
+50

然而,这有一个缺点,我需要在源文件中重述别名:

这是不正确的。你只需要使它 public 然后指定适当的范围,所以你在 Bar 范围之外调用 Bar :: FooTable (其中包括返回类型,除非尾随!):

  Bar :: FooTable Bar :: create_foo(){/ * ... * /}   

  auto Bar :: create_foo() - > FooTable {/ * ... * /}   

(只需 FooTable 就可以了 这个定义,因为它是一个会员!)

你的方法很好,虽然我也把所有内容放在命名空间中。那么你的别名是否在课堂上并不重要:它仍然是你自己的代码中的自包含。它纯粹是一种风格问题,对其他任何人都没什么影响。

+20

但现在我读到使用这种方式使用可能会有问题,因为它会强制包含此标题的所有内容。

此问题同样适用于您定义的class Bar 包含此标头的所有程序都必须使用 Bar 的此定义。

所以,我只是把使用放在类中

你减少了全局命名空间中的声明数量。这很好。

但是这有一个缺点,我需要在源文件中重新设置别名

这是不必要的。

如果您将别名公之于众,您可以使用范围解析运算符将其引用为 Bar :: FooTable 或者您可以使用尾随返回类型,其中在类的范围内查找上下文名称:

  auto Bar :: create_foo() - > FooTable   

有一个更通用的解决方案:命名空间。将所有自己的声明放入单个命名空间(可以进一步划分为子命名空间)。这样,您只需在全局命名空间中引入一个名称,这可以大大减少名称冲突的可能性。

在C ++头文件中使用指令的位置

相同推理适用于您放置任何其他声明的地方。至少进入你自己的命名空间,但通常将声明放在一个狭窄的范围内就足够了,这是一个不错的经验法则。如果类型别名仅用于那个类,那么成员类型别名很有意义。

+10

正如评论中已经提到的,您无需重新声明它。如果在类中声明它,只需使用 Bar :: FooTable 来引用它。如果您在命名空间中声明它,那就是一样的,除非您在命名空间之外使用命名空间名称。如果你使用typedef也是一样的。

无论是在命名空间还是在类中声明它都完全取决于您。就个人而言,我尽量确保其范围尽可能相关。例如,如果我有一个仅用于连接特定类的typedef,我将typedef放在类中。如果它具有与特定类无关的全局值,我将其声明在命名空间中。

话虽如此,我建议你不要 t在全局命名空间中声明它以避免歧义如果你出于某种原因发现自己有一个命名冲突,如果你最终得到一个不同的typedef(或者我认为其他一般与你的typedef / using语句同名)在某处声明另外,

此外,类中的typedef受访问修饰符的限制。默认情况下,它是私有的,这意味着你不能在课外使用它。如果您打算这样做,您需要将其公之于众。

在安全性方面,在全局范围内声明它并不是特别安全,特别是如果将它与 using namespace 结合使用(这可能是它自己的问题 - 请参阅< a href =“https://stackoverflow.com/a/1452738/6296561”>这个)。您可以在自己的命名空间中声明它( 命名空间Baz {使用FooTable = blah; / *更多代码* /} ),但声明它是一个类会产生相同的效果。

请注意,名称空间和类本质上是作用域,它们有自己的动态。如果在命名空间Baz 中的源文件中编写代码,则可以访问在同一命名空间中声明的typedef,而不指定 Baz :: FooTable 它本质上暴露了typedef,类似于它在全局命名空间中的工作方式,但是以更受限制的方式。有关此处的更多信息。

请注意,名称空间和类本质上是作用域,它们有自己的动态。如果在命名空间Baz 中的源文件中编写代码,则可以访问在同一命名空间中声明的typedef,而不指定 Baz :: FooTable 它本质上暴露了typedef,类似于它在全局命名空间中的工作方式,但是以更受限制的方式。有关此处的更多信息。

请注意,名称空间和类本质上是作用域,它们有自己的动态。如果在命名空间Baz 中的源文件中编写代码,则可以访问在同一命名空间中声明的typedef,而不指定 Baz :: FooTable 它本质上暴露了typedef,类似于它在全局命名空间中的工作方式,但是以更受限制的方式。有关此处的更多信息。 您可以访问在同一名称空间中声明的typedef,而无需指定 Baz :: FooTable 它本质上暴露了typedef,类似于它在全局命名空间中的工作方式,但是以更受限制的方式。有关此处的更多信息。 您可以访问在同一名称空间中声明的typedef,而无需指定 Baz :: FooTable 它本质上暴露了typedef,类似于它在全局命名空间中的工作方式,但是以更受限制的方式。有关此处的更多信息。

10
votes
answers
24 views
+10

为什么这个C ++折叠表达式有效?

cppreference 上,我看到有四种类型的折叠表达式,一元右,一元左,二元右,二元左。这个折叠表达式的类型是什么?我很难理解为什么它有效。

  template&lt; typename Res,typename ... Ts&gt; 矢量&lt; RES&GT; to_vector(Ts&amp; ... ts){vector&lt; Res&gt; VEC; (vec.push_back(ts)...); // *返回vec; }   

行*中“pack”,“op”和“init”的值是多少?

此示例来自Bjarne的第244页Stroustrup的

up vote 10 down vote accepted favorite
沙发
+100
+50

语法无效。它缺少一个逗号:

 (vec.push_back(ts),...)// ^   

因此它是“一元右折” :

 (pack op ...)  

op 是逗号。

11
votes
answers
34 views
+10

Task on the number of iterations

There is a number N every iteration it becomes equal to (N*2)-1 I need to find out how many steps the number will be a multiple of the original N;

( 1≤ N ≤ 2 · 10 9 )

For example:

N = 7; count = 0

N_ = 7*2-1 = 13; count = 1; N_ % N != 0

N_ = 13*2-1 = 25; count = 2; N_ % N != 0

N_ = 25*2-1 = 49; count = 3; N_ % N == 0 

Answer is 3

if it is impossible to decompose in this way, then output -1

       #include <iostream> 
       using namespace std;

       int main(){
           int N,M,c;
           cin >> N;
           if (N%2==0) {
               cout << -1;
               return 0;
           }
           M = N*2-1;
           c = 1;
           while (M%N!=0){
               c+=1;
               M=M*2-1;
           }
           cout << c;
           return 0;
       }

It does not fit during (1 second limit). How to optimize the algorithm?

P.S All the answers indicated are optimized, but they don’t fit in 1 second, because you need to change the algorithm in principle. The solution was to use Euler's theorem.

沙发
+50

正如其他答案所暗示的那样,问题等同于找到 c ,使 pow(2,c)= 1 mod N 如果N是偶数,则这是不可能的,否则可能(如您的代码所暗示的那样)。

线性时间方法是:

  int c = 1; uint64_t m = 2; while(m!= 1){c + = 1; m =(2 * m)%N; } printf(“%d”,c);   

要在1秒内解决这个问题,我认为你不能使用线性时间算法。最糟糕的情况是 N 是素数大而且很大。例如1999999817,上面的代码在我的笔记本电脑上运行大约10秒钟。

相反,将 N 纳入其主要因素。求解 2 ^ c = 1 mod p ^ k 对于给定的素数幂,如果 k = 1 ,则解决方案是 c = p-1 k 更大时,细节非常混乱,但你可以在这里找到一个书面解决方案: https://math.stackexchange.com/questions/1863037/discrete-logarithm-modulo-powers-of-a-small-prime 对于给定的素数幂,如果 k = 1 ,则解决方案是 c = p-1 k 更大时,细节非常混乱,但你可以在这里找到一个书面解决方案: https://math.stackexchange.com/questions/1863037/discrete-logarithm-modulo-powers-of-a-small-prime

板凳
+40

问题是你溢出,int数据类型只有32位,溢出2 ^ 31-1,在这个问题你不需要保持M的实际值,你可以保持模数n。

  while(M%N!= 0){c + = 1; M = M * 2-1; M%= N}   

编辑:此外,你实际上不需要超过N次迭代来检查是否存在0 mod,因为N只有N个不同的mod并且它只是骑自行车。因此,如果没有0 mod,你还需要牢记这一点。

如果有溢出,那么测试就没有错误的答案,但我有一个与时间相关的错误 - yoloy 8月29日5:38

这当然是你迈出的一大步,但它并没有增加速度 - yoloy 8月29日5:39

@yoloy你怎么知道法官在收到错误答案之前没有放弃?这可能是也可能不是整个问题,但它是一个错误。 - user4581301 8月29日5:39

旁注:在M%= N的情况下,循环条件中的M%N基本上是冗余的。 - user4581301 8月29日5:40

当你溢出时,while循环不会结束,因为mod不会达到0,数字将变为负数并且继续溢出,这导致超出时间限制 - Malek Aug 29 at 5:42

地板
+20

毫无疑问,您的代码的主要问题是有符号整数溢出

每当 M时,我都会添加 M 的打印件已更改(即 cout&lt;&lt; M&lt;&lt; endl; )并给它输入29.这就是我得到的:

  57 113 225 449 897 1793 3585 7169 14337 28673 57345 114689 229377 458753 917505 1835009 3670017 7340033 14680065 29360129 58720257 117440513 234881025 469762049 939524097 1879048193 -536870911 -1073741823 -2147483647 1 1 1 ...无限循环  

如您所见,您已经签署了整数溢出。这是C中的未定义的行为,所以任何事情都可能发生!在我的机器上,我结束了一个讨厌的无限循环。在考虑性能之前必须先修复它。

简单的解决方法是添加一行,如

  M = M%N;   

每当 M 被更改时。请参阅@Malek的答案

除此之外,您还应使用无符号整数,即对所有变量使用 uint32_t

但是,这将提高性能。

如果在上述修复后仍有性能问题,您可以尝试这样做:

  uint32_t N; cin&gt;&gt; N; if(N%2 == 0){cout&lt;&lt; -1; 返回0; } //替代算法uint32_t R,c; R = 1; c = 1; 而(R!= N){R = 2 * R + 1; if(R> N)R = R-N; ++℃; } cout&lt;&lt; C;   

在我的笔记本电脑上,当测试1..100000范围内的所有奇数时,此算法的速度要快2.5倍。但是,对于1..2 * 10 ^ 9范围内的所有数字可能还不够。

还要注意使用 uint32_t 来避免整数溢出。

13
votes
answers
32 views
+10

从lambda返回变体

我有这个简单的lambda:

  std :: variant&lt; int,char&gt; myLambda = [](){//没有合适的用户定义的从“type”到“std :: variant&lt; int,char&gt;”的转换 存在std :: variant&lt; int,char&gt; 水库; if(true){res = 1; } else {res ='c'; } return res; };   

但是它没有编译,产生错误没有合适的用户定义的转换,从“type”到“std :: variant&lt; int,char&gt;” 存在我做错了什么?

沙发
+60
+50

lambda表达式类型错误。您正在尝试绑定到 std :: variant&lt; int,char&gt; Lambda表达式类型是 ClosureType 使用 auto

  auto processProjectFile = [](){std :: variant&lt; int,char&gt; 水库; if(true){res = 1; } else {res ='c'; } return res; };   

或者,你可以将 ClosureType 强制转换为 std :: function ,用 std替换 auto :: function&lt; std :: variant&lt; int,char&gt;(void)&gt;

但是如果你打算调用lambda,只需替换}; 最后由

是的,这是我错过的。谢谢。我遇到过的愚蠢错误。BTW std :: function部分很方便!谢谢! - Nurbol Alpysbayev 8月30日12:32

不确定ClosureType是什么意思。一个lambda类型的名称是impl-defined - Lightness Races in Orbit 8月30日13:27

+70

要么是指

  std :: variant&lt; int,char&gt; v = [](){std :: variant&lt; int,char&gt; 水库; if(true){res = 1; } else {res ='c'; } return res; }(); ^^^   

或者你的意思是

  auto myLambda = [](){std :: variant&lt; int,char&gt; 水库; if(true){res = 1; } else {res ='c'; } return res; };   

Lambda表达式具有唯一类型。

11
votes
answers
20 views
+10

在C ++中为3D数组分配连续内存

我能够分配连续内存来在C ++中分配2D数组。我不知道如何处理3D阵列。我已经阅读了一些帖子,但我无法提出解决方案。

  #include&lt; iostream&gt; using namespace std; int main(int argc,char ** argv){cout&lt;&lt; “Ints具有大小”&lt;&lt; sizeof(int)&lt;&lt; ENDL; int rows = 2; int cols = 3; int ** i = new int * [rows]; int size = rows * cols; i [0] = new int [size]; for(int j = 1; j&lt; rows; j ++){i [j] =&amp; i [0] [j * cols]; for(int j = 0; j&lt; rows; j ++){for(int k = 0; k&lt; cols; k ++){cout&lt;&lt; j&lt;&lt; “”&lt;&lt; k&lt;&lt; “”&lt;&lt; &amp; i [j] [k]&lt;&lt; ENDL; 删除[]我; 返回0; }  
    
        
沙发
+70

带有 P 平面的3d数组,每个都有 R 行和 C 列,需要 P * R * C 元素。您可以一次分配它们,例如:

 元素* p =新元素[P * R * C];   

然后访问坐标(p,r,c)的元素,你可以使用公式:

  int index =(p * R + r)* C + c;   

为了使事物可读,一个简单的解决方案是创建一个类

 模板&lt; typename T&gt; struct Matrix3D {int P,R,C; 的std ::矢量&lt; T&GT; 要素; Matrix3D(int P,int R,int C):P(P),R(R),C(C),元素(P * R * C){} T&amp; operator()(int p,int r,int c){return elements [(p * R + r)* C + c]; }};   

在这个例子中,我使用 std :: vector 来存储元素,因为这使得所有权/复制变得更简单,并且仍然保证所有元素都是在记忆中连续。如果要手动分配存储,则需要更多代码。

如果在编译时已知大小,则可以使 P R C 模板参数,并使用 std :: array 成员而不是 std :: vector

板凳
+20

数组(3D数组)的数组只不过是一个数组,它包含每个索引中另一个数组的引用。
你只需要分配你的第一个<代码> 2D数组,然后,对于此数组的每个索引,在其中分配另一个数组。

3D数组也可以是三维数组,任何地方都没有指针。您所描述的通常称为“锯齿状阵列”,因为不同的行可以具有不同的大小。 - 昨天的海德

那是真的是的!但是用户没有特别要求使用“classicé数组。他可以使用for循环分配索引成员,并且只为每个成员使用常量作为大小。” - cocool97昨天

问题要求“连续记忆”。 - 昨天的海德

@hyde Continous意味着在内存中只占用一个空间范围,而不是所有的colums必须做同样的大小不是吗? - 昨天的cocool97

@ cocool97如果你做指针然后为它们分配内存,整个内存的内存将不会是连续的 - 特别是如果数组大于一个虚拟页面。 - 杰里耶利米昨天

地板
+20

如果你必须分配的空间必须是连续的,那么它必须分配一个'new',否则内存将不会是连续的。

这看起来像这个:

  int d1 = 10; // first int d2 = 10; // second int d3 = 10; //第三维int * array3D = new int [d1 * d2 * d3];   

你已经为你的3D数组分配了enoug空间,现在必须将它映射到3D。

  array3D [(1 * d1 * d2) )+(2 * d2)+(3)]; //访问元素1,2,3   

使用此功能,您可以将已分配的此1D数组的每个点映射到3D空间中的唯一点。

您可能会发现这非常容易出错。所以你不应该这样做。< / p>

不要使用new / delete来分配这样的数组:

使用 std:array std :: vector 来为你处理这个。使用raw new / delete会导致错误,如果有任何东西被分配了new而你忘了删除它,或者你忽略了什么,就会有内存泄漏。

  void test() {int * a = new int [20]; //做一些... if(错误)返回; // oops这是一个泄漏删除a; //仅在没有错误的情况下执行,如果你知道数组在编译时有多大,那么将使用}   

std :: array ,它永远不必改变。

std :: vector 另一方面,如果您在编译时不知道大小,可以使用它,它可以在您的程序运行时更改。

  std :: array&lt; int,10&gt; test1的; //创建一个大小为10的固定大小的数组并输入int。的std ::矢量&lt; INT&GT; TEST2(10); //创建一个可以在运行时更改的数组:test2.push_back(2); //向量现在的大小为11,最后一个元素等于2   

这样,您也不必在末尾删除数组。

如果您希望能够在代码中更频繁地使用它,将所有这些功能包装在一个类中会非常有用:

  #include&lt;阵列&GT; template&lt; typename T,std :: size_t _D1,std :: size_t _D2,std :: size_t _D3&GT; class Array3D {std :: array&lt; T,_D1 * _D2 * _D3&gt; 要素; public:std :: size_t D1(){return _D1; } std :: size_t D2(){return _D1; } std :: size_t D3(){return _D1; T&amp; element(std :: size_t d1,std :: size_t d2,std :: size_t d3){return elements [(d1 * _D1 * _D2)+(d2 * _D2)+(d3)]; }}; int main(){//如果不使用它们,则不需要argc / argv Array3D&lt; int,10,10,10&gt; 阵列; array.element(1,2,3)= 5; //循环遍历所有元素//方法d1,d2,d3返回你初始给它们的维度//这样你就可以根据数组大小来改变这个循环(std :: size_t i = 0 ; i&lt; array.D1(); i ++)for(std :: size_t j = 0; j&lt; array.D2(); j ++)for(std :: size_t k = 0; k&lt; array.D3( );; k ++)数组。element(i,j,k)= 5; //没有删除}  
     
			
        
14
votes
answers
34 views
+10

标准是否保证uint8_t,int8_t和char都是唯一类型?

以下似乎保证通过(已经此处):< / p>

  #include&lt; type_traits&gt; static_assert(!std :: is_same_v&lt; char,signed char&gt;); static_assert(!std :: is_same_v&lt; char,unsigned char&gt;);   

引用 cppreference

[ char ]具有与 signed char unsigned char 相同的表示和对齐方式,但是总是一个独特的类型

是否还保证 int8_t uint8_t 根据显式签名类型 而不是定义的在 char 方面,因此也使用 char 形成一组3个不同的类型?

  #include&lt; cstdint&gt; #include&lt; type_traits&gt; static_assert(!std :: is_same_v&lt; char,int8_t&gt;); static_assert(!std :: is_same_v&lt; char,uint8_t&gt;);   因此也可以用 char  
  #include&lt; cstdint&gt;形成一组3种不同的类型。#include&lt; type_traits&gt; static_assert(!std :: is_same_v&lt; char,int8_t&gt;); static_assert(!std :: is_same_v&lt; char,uint8_t&gt;);   因此也可以用 char  
  #include&lt; cstdint&gt;形成一组3种不同的类型。#include&lt; type_traits&gt; static_assert(!std :: is_same_v&lt; char,int8_t&gt;); static_assert(!std :: is_same_v&lt; char,uint8_t&gt;);  
    
        
沙发
+70

关于你的第一点,是的, char signed char unsigned char 必须始终是不同的类型。 < p>关于第二点, int8_t uint8_t 可能与 char (或其签名)的类型相同或不同 unsigned 变体); 即无法保证或反对。

板凳
+70

固定宽度类型是实现定义的别名。(u)int8_t 类型根本不保证是任何基本 char 类型的别名。它们仅保证是(未)签名的8位整数类型。它们可能是(un)signed char 的别名,或者它们可能是特定于供应商的类型的别名,例如(un)signed __int8 由每个编译器供应商决定哪些别名最适合其实现。

我更新了我的问题 - 我希望的是保证它们不是非限定字符的别名,因为如果它们是我不能专门为char设置模板而不匹配其中一种类型。 - 埃里克8月29日7:22

@Eric:不,没有这样的保证:typedef char uint8_t; 如果char是无符号的,我会做的,虽然我认为一个合理的实现会使用typedef unsigned char uint8_t;。 - Bathsheba 8月29日7:26

在某种程度上,我认为要求合理的实施是标准的工作:)。我认为这是一个非常小的刺,花费委员会时间虽然... - 埃里克8月29日8:05

@Eric什么是精确的问题,如果char专业化也涵盖了这两种类型中的一种?如果你为uint8_t和int8_t添加另外两个特化,它会发生冲突吗?这对我来说听起来像是一个真正的问题,但我希望编译器通过检查特化的特定类型并将它们全部作为不同的类型来处理这种歧义。clang 8.0.0似乎这样做:godbolt.org/z/gphsmB(对我而言,这看起来没有问题,因为如果你的代码想要专门用于某种类型,它可以) - Ped7g 8月29日8:52

15
votes
answers
32 views
+10

如果堆栈在数字较低的地址增长,为什么指针比较会反转?

由于堆栈向下增长,即朝向数值较小的存储器地址,为什么&amp; i&lt; &amp; j 是真的。如果我错了,请纠正我,但我想这是C创作者的设计决定(C ++维护)。但我想知道为什么。

同样奇怪的是,堆分配的对象 pin 位于比堆栈变量更高的内存地址,这也与堆的事实相矛盾位于数量上比堆栈小的内存地址(并向上增加)。

  #include&lt; iostream&gt; int main(){int i = 5; // stack assigned int j = 2; // stack assigned int * pi =&amp; i; // stack assigned int * pj =&放大器;焦耳; //堆栈已分配std :: cout&lt;&lt; std :: boolalpha&lt;&lt; ''; std :: cout&lt;&lt; (&amp; i&amp; j)&amp;&amp; (pi&lt; pj)&lt;&lt; ''; // true struct S {int in; }; S * pin // stack allocated =新S {10}; //堆分配std :: cout&lt;&lt; ''&lt;&lt; (&amp;(pin-&gt; in)&gt;&amp; i)&lt;&lt; ''; // true std :: cout&lt;&lt; ((void *)pin&gt;(void *)pi)&lt;&lt; ''; // true}   

到目前为止,我是否正确,如果是这样,为什么C设计师扭转了这种情况,数值较小的内存地址显得更高(至少在比较指针或通过运算符&amp; 的地址时)。这样做只是为了“让事情有效”吗?

沙发
+70
+50

如果我错了,请纠正我,但我想这是C创作者的设计决定

它不是C语言设计的一部分,也不是C ++。事实上,没有这些标准认可的“堆”或“堆栈”内存。

这是一个实现细节。每种语言的每种实现都可以这样做。


指向不相关对象的指针之间的有序比较,例如&amp; i&lt; &amp; j (void *)pin&gt; (void *)pi 具有未指定的结果。两者都不会保证比另一个更少或更大。

对于它的价值,你的示例程序输出三个“假”计数

+70

编译器生成的代码不是按顺序为每个单独的变量分配空间,而是为这些局部变量分配,因此可以在它选择的那个块中排列它们。

+10

通常,在函数输入期间,一个函数的所有局部变量都被分配为一个块。因此,如果将外部函数中分配的局部变量的地址与内部函数中分配的局部变量的地址进行比较,则只会看到堆栈向下增长。

12
votes
answers
34 views
+10

如何深度复制unique_ptr的向量

我有一个带有数据成员的类。

  std :: vector&lt; 的std ::的unique_ptr&LT; Sum_Function&GT; &GT; 功能;   

我想在我的复制构造函数中做一个深层复制,我该如何做一个std :: unique_ptr的深层复制。

沙发
+40
+50
<预> <代码>的std ::矢量&lt;标准::的unique_ptr&LT; Sum_Function&GT;&GT; copiedFunctions; std :: for_each(Functions.begin(),Functions.end(),[&amp;](std :: unique_ptr&lt; Sum_Function&gt; f){copiedFunctions.push_back(std :: make_unique&lt; Sum_Function&gt;(* f));} ));

这意味着 Sum_Function 当然有一个复制构造函数。

只是旁注,这不是一个人应该如何使用for_each()(因为如果没有选择执行策略它有一个返回值),基于范围的for或transform()更适合选择。 - 克罗曼8月29日10:31

修改结果向量的副本大多没有意义。 - >通过引用捕获。 - Jarod42 8月29日13:51

+40

由于你无法复制 std :: unique_ptr ,你需要手动复制所有元素,我认为最好的方法是 std :: transform

  std :: vector&lt; std :: unique_ptr&lt; Sum_Function&gt;&gt; 复制; copy.reserve(Functions.size()); std :: transform(Functions.cbegin(),Functions.cend(),std :: back_inserter(copy),[](const std :: unique_ptr&lt; Sum_Function&amp;&gt; ptr){return std :: make_unique&lt; Sum_Function&gt;( * ptr);});   

此代码假定 Sum_Function 具有复制构造函数。

+40

如果 Sum_Function 是您要复制的具体类型:

  #include&lt; algorithm&gt; 容器(const容器和其他):函数(other.Functions.size()){std :: transform(other.Functions.cbegin(),other.Functions.cend(),Functions.begin(),[](const auto&amp; uPtr){return uPtr?std :: make_unique&lt; Sum_Function&gt;(* uPtr):nullptr;}); }   

否则(例如,当 Sum_Function 是一个抽象基类时),你需要一个 virtual 工厂成员函数 Sum_Function: :clone(),应该在lambda中调用。

10
votes
answers
24 views
+10

g ++和clang ++不同的行为推导可变参数模板`auto`值

另一个“g ++和clang ++之间谁是正确的?”

这次我确信这是一个g ++错误,但我要求标准专家确认。

给出以下代码

 模板&lt; template&lt; auto ...&gt; class Cnt,typename ...类型,类型... Vals&gt; void foo(Cnt&lt; Vals ...&gt;){}模板&lt; auto ...&gt; struct bar {}; int main(){foo(bar&lt; 0,1&gt; {}); //编译两个foo(bar&lt; 0,1L&gt; {}); //只有clang ++编译; 来自g ++的错误}   

现场演示 < p> clang ++(8.0.0,通过示例)编译和链接没有问题g ++(9.2.0,通过示例)给出以下错误编译第二个 foo()(但不是第一个)调用

 < code> prog.cc:在函数'int main()'中:prog.cc:16:20:错误:没有匹配函数来调用'foo(bar&lt; 0,1&gt;)'16 | foo(bar&lt; 0,1L&gt; {}); //只有clang ++编译; 来自g ++ |的错误 ^ prog.cc:6:6:注意:候选人:'模板&lt;模板&lt; auto ...&lt;匿名&gt; &GT; class Cnt,class ...类型,类型... Vals&gt; void foo(Cnt&lt; Vals ...&gt;)'6 | void foo(Cnt&lt; Vals ...&gt;)| ^ ~~ prog.cc:6:6:注意:模板参数推断/替换失败:prog.cc:16:20:注意:不匹配的类型'int' 和'long int'16 | foo(bar&lt; 0,1L&gt; {}); //只有clang ++编译; 来自g ++ |的错误 ^ prog.cc:16:20:注意:'bar&lt; 0,1&gt;' 不是源于'Cnt&lt; Vals ...&gt;'   

如果我理解正确,g ++要求 Vals ... Types ... 重合,其中clang ++接受 Vals ... 是不同的 Types ...

谁是对的?

- 编辑 -

正如Marek R指出的那样(感谢),MSVC(v19.22)也无法编译。 <但是,如果我理解正确,那么也会编译第一个 foo() 使用以下错误调用

 &lt; source&gt;(13):错误C2672:'foo':找不到匹配的重载函数&lt; source&gt;(13):错误C2893:无法专门化函数template'void foo(Cnt&lt; Vals ...&gt;)'&lt; source&gt;(13):注意:使用以下模板参数:&lt; source&gt;(13):注意:'Cnt = bar'&lt; source&gt; (13):注意:'Types = {}'&lt; source&gt;(13):注意:'Vals = {0,1}'  

- 编辑2 - -

camp0观察(谢谢)g ++编译此代码直到版本7.4。

从8.1引入的错误或者我的代码和g ++已经纠正了他的代码8.1? source&gt;(13):error C2672:'foo':找不到匹配的重载函数&lt; source&gt;(13):error C2893:无法专门化函数模板'void foo(Cnt&lt; Vals ...&gt;)'&lt;来源&gt;(13):注意:使用以下模板参数:&lt; source&gt;(13):注意:'Cnt = bar'&lt; source&gt;(13):注意:'Types = {}'&lt; source&gt;( 13):注意:'Vals = {0,1}'

- 编辑2 -

camp0观察(谢谢) g ++编译此代码直到版本7.4。

从8.1引入的错误或者我的代码和g ++已经修改了他的代码从8.1开始? source&gt;(13):error C2672:'foo':找不到匹配的重载函数&lt; source&gt;(13):error C2893:无法专门化函数模板'void foo(Cnt&lt; Vals ...&gt;)'&lt;来源&gt;(13):注意:使用以下模板参数:&lt; source&gt;(13):注意:'Cnt = bar'&lt; source&gt;(13):注意:'Types = {}'&lt; source&gt;( 13):注意:'Vals = {0,1}'

- 编辑2 -

camp0观察(谢谢) g ++编译此代码直到版本7.4。

从8.1引入的错误或者我的代码和g ++已经修改了他的代码从8.1开始? 无法专门化功能模板'void foo(Cnt&lt; Vals ...&gt;)'&lt; source&gt;(13):注意:使用以下模板参数:&lt; source&gt;(13):注意:'Cnt = bar' &lt; source&gt;(13):注意:'Types = {}'&lt; source&gt;(13):注意:'Vals = {0,1}'

- - 编辑2 -

camp0观察(谢谢)g ++编译此代码直到版本7.4。

从8.1引入的错误或者我的代码和g ++有错误从8.1修正了他的代码? 无法专门化功能模板'void foo(Cnt&lt; Vals ...&gt;)'&lt; source&gt;(13):注意:使用以下模板参数:&lt; source&gt;(13):注意:'Cnt = bar' &lt; source&gt;(13):注意:'Types = {}'&lt; source&gt;(13):注意:'Vals = {0,1}'

- - 编辑2 -

camp0观察(谢谢)g ++编译此代码直到版本7.4。

从8.1引入的错误或者我的代码和g ++有错误从8.1修正了他的代码? 'types = {}'&lt; source&gt;(13):注意:'Vals = {0,1}'

- 编辑2 - < / p>

camp0观察(谢谢)g ++编译此代码直到版本7.4。

从8.1引入的错误或者我的代码和g ++已经纠正了8.1的代码? 'types = {}'&lt; source&gt;(13):注意:'Vals = {0,1}'

- 编辑2 - < / p>

camp0观察(谢谢)g ++编译此代码直到版本7.4。

从8.1引入的错误或者我的代码和g ++已经纠正了8.1的代码?

up vote 10 down vote accepted favorite
沙发
+100
+50

三个编译器中没有一个是正确的。

来自 [temp.param] / 17

如果 template-parameter 是带有省略号的 type-parameter 在其可选的标识符之前或是声明包([dcl.fct])的参数声明之前, template-parameter 是模板参数包。模板参数包是参数声明,其类型包含一个或多个未展开的包,是包扩展。...... 作为包扩展的模板参数包不得扩展在同一 template-parameter-list 中声明的模板参数包。[例如:

...

  template&lt; class ... T,T ... Values&gt; //错误:值扩展模板类型参数struct static_array; //在相同的模板参数列表中打包  

- 结束示例]

所以即使没有行 foo(bar&lt; 0,1L&gt; {});

已经有一个Clang 错误报告和GCC < a href =“https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67397”rel =“nofollow noreferrer”>错误报告。

@康桓瑋 - Regarding functions, what you observed seems confirmed in my new question. Anyway, I am increasingly confused. - max66 Aug 28 at 21:17

@ max66更新了答案。希望它现在有用。 - xskxzr 8月29日2:39

14
votes
answers
26 views
+10

如何创建具有可变数量类型参数的模板?

在我们的代码库中,我们使用 std :: variant&lt; std :: shared_ptr&lt; SomeClass&gt;,...&gt;

这显然需要大量的写作。如何制作模板?

  template&lt; class ... T&gt; 使用VarSP = std :: variant&lt; std :: shared_ptr&lt; ???&gt;&gt ;;   

T 应该放在上面的代码片段中?期望的行为应该是:

  VarSP&lt; Foo,Bar,Baz&gt; // std :: variant&lt; std :: shared_ptr&lt; Foo&gt;,std :: shared_ptr&lt; Bar&gt ;,std :: shared_ptr&lt; Baz&gt;&gt;  
    
        
up vote 14 down vote accepted favorite
沙发
+140
+50
  template&lt; typename ... T&gt; 使用VarSP = std :: variant&lt; std :: shared_ptr&lt; T&gt; ...&gt ;;