考虑类模板,可以使用类型特征和虚拟启用模板参数为某些类型的组提供模板特化.我早些时候已经问过了.
现在,我对函数模板需要相同的东西:即,我有一个模板函数,并希望对一组类型进行特化,例如,所有类型都是类的子类型X.我可以用这样的类型特征来表达这个:
std::enable_if<std::is_base_of<X, T>::value>::type
Run Code Online (Sandbox Code Playgroud)
我想过这样做:
template <typename T, typename ENABLE = void>
void foo(){
//Do something
}
template <typename T>
void foo<T,std::enable_if<std::is_base_of<A, T>::value>::type>(){
//Do something different
}
Run Code Online (Sandbox Code Playgroud)
但是,这不起作用,因为功能模板不允许部分特化.那么怎么做呢?也许类型为trait的默认参数为type?但是代码怎么样呢?
编写以下内容时,编译器为什么不抱怨:
#include "stdio.h"
Run Code Online (Sandbox Code Playgroud)
不应该
#include <stdio.h>
Run Code Online (Sandbox Code Playgroud)
相反,因为stdio.h实际上存储在库文件夹中而不是存储在翻译单元的文件夹中?为什么它仍然有效?
通常的模板结构可以是专门的,例如,
template<typename T>
struct X{};
template<>
struct X<int>{};
Run Code Online (Sandbox Code Playgroud)
C++ 11为我们提供了using表达模板typedef 的新酷语法:
template<typename T>
using YetAnotherVector = std::vector<T>
Run Code Online (Sandbox Code Playgroud)
有没有办法使用类似于结构模板的特化的构造为这些定义模板特化?我尝试了以下方法:
template<>
using YetAnotherVector<int> = AFancyIntVector;
Run Code Online (Sandbox Code Playgroud)
但它产生了编译错误.这有可能吗?
是否有一种方法(例如,定义的常量)来访问编译器在编译的代码中运行的编译标志.
例如,我想要一个程序来编写用于编译它的标志.
int main(){
std::cout << COMPILE_FLAGS << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
gcc/g ++是否存在这样的常量?甚至更好:是否有在gcc和clang中定义的常量?
我特别感兴趣的是检查优化级别和-march标志的值.那么,如果没有显示所有标志的常量,那么至少有哪些显示这些值?
我刚刚测试了一个小例子来检查__restrict__C++ 是否适用于最新的编译器:
void foo(int x,int* __restrict__ ptr1, int& v2) {
for(int i=0;i<x;i++) {
if(*ptr1==v2) {
++ptr1;
} else {
*ptr1=*ptr1+1;
}
}
}
Run Code Online (Sandbox Code Playgroud)
当使用最新的gcc(gcc8.1 -O3 -std = c ++ 14)在godbolt.org上尝试时,__restrict__按预期工作:v2只加载一次,因为它不能用别名ptr1.
以下是相关的装配部件:
.L5:
mov eax, DWORD PTR [rsi]
cmp eax, ecx # <-- ecx contains v2, no load from memory
jne .L3
add edx, 1
add rsi, 4
cmp edi, edx
jne .L5
Run Code Online (Sandbox Code Playgroud)
现在与最新的clang相同(clang 6.0.0 -O3 -std = c ++ 14).它将循环展开一次,因此生成的代码要大得多,但这里是要点:
.LBB0_3: # …Run Code Online (Sandbox Code Playgroud) 我正在尝试构建Java到C++的转换器(即Java代码进入,语义上"等效"(或多或少)C++代码出来).
不考虑垃圾收集,语言非常熟悉,因此整个过程已经很好用了.然而,一个问题是C++中不存在的泛型.当然,最简单的方法是执行java编译器所做的擦除.但是,生成的C++代码应该很好处理,所以如果我不丢失泛型类型信息会很好,也就是说,如果C++代码仍然可以使用List<X>而不是List.否则,C++代码需要在使用此类泛型的任何地方进行显式转换.这很容易出错并且不方便.
所以,我试图找到一种方法来以某种方式获得更好的泛型代表.当然,模板似乎是一个很好的候选人.尽管它们是完全不同的(元编程与仅编译时类型增强),但它们仍然有用.只要不使用通配符,只需将通用类编译为模板即可.然而,只要通配符发挥作用,事情就会变得非常混乱.
例如,考虑以下列表的java构造函数:
class List<T>{
List(Collection<? extends T> c){
this.addAll(c);
}
}
//Usage
Collection<String> c = ...;
List<Object> l = new List<Object>(c);
Run Code Online (Sandbox Code Playgroud)
怎么编译呢?我有想法在模板之间使用电锯重新解释.然后,上面的例子可以这样编译:
template<class T>
class List{
List(Collection<T*> c){
this.addAll(c);
}
}
//Usage
Collection<String*> c = ...;
List<Object*> l = new List<Object*>(reinterpret_cast<Collection<Object*>>(c));
Run Code Online (Sandbox Code Playgroud)
然而,问题是这种重新解释演员是否会产生预期的行为.当然,它很脏.但它会起作用吗?通常,List<Object*>并且List<String*>应该具有相同的内存布局,因为它们的模板参数只是一个指针.但这有保证吗?
我想到的另一个解决方案是通过模板方法替换使用通配符的方法,这些方法实例化每个通配符参数,即将构造函数编译为
template<class T>
class List{
template<class S>
List(Collection<S*> c){
this.addAll(c);
}
}
Run Code Online (Sandbox Code Playgroud)
当然,所有其他涉及通配符的方法addAll都需要模板参数.这种方法的另一个问题是例如在类字段中处理通配符.我不能在这里使用模板.
第三种方法是混合方法:将泛型类编译为模板类(调用它T<X>)和擦除类(调用它E).模板类T<X>继承E自已擦除的类,因此始终可以通过向上转换为E来删除通用性.然后,所有包含通配符的方法都将使用擦除类型进行编译,而其他方法可以保留完整的模板类型.
你怎么看待这些方法?你在哪里看到他们的优势?您是否有任何其他想法可以尽可能地将通配符实现为尽可能干净,同时尽可能在代码中保留通用信息?
是否可以编写仅用于类类型的部分模板特化,例如,从特定类继承或遵守可通过类型特征表达的其他约束?即,像这样:
class A{}
class B : public A{}
template<typename T>
class X{
int foo(){ return 4; }
};
//Insert some magic that allows this partial specialization
//only for classes which are a subtype of A
template<typename T>
class X<T>{
int foo(){ return 5; }
};
int main(){
X<int> x;
x.foo(); //Returns 4
X<A> y;
y.foo(); //Returns 5
X<B> z;
z.foo(); //Returns 5
X<A*> x2;
x2.foo(); //Returns 4
}
Run Code Online (Sandbox Code Playgroud) 我偶然发现了segvcatch库,它承诺将segfaults和浮点错误包装到适当的异常中.
使用这个库是安全的,如果我添加了捕获的所有段错误只会是空指针访问的前提条件(即,没有数组溢出或无效指针可能在segfaulting之前完全搞砸了内存,导致未定义的行为无论如何)?在捕获nullptr段错误后,程序是否仍然定义了语义?浮点错误呢?他们表现得更好/不同吗?
旁注:请不要评论说任何产生段错误的程序都是格式错误的,应该进行调试/修复.我知道,我同意.不过,我对这个问题很感兴趣.
我偶然发现了一个问题,询问你是否曾经不得不在实际项目中使用位移.我在许多项目中都使用了相当广泛的位移,但是,我从来不必使用算术位移,即位移,其中左操作数可能是负的,符号位应该移位而不是零.例如,在Java中,您将使用运算>>符进行算术位移(同时>>>执行逻辑移位).在经过深思熟虑之后,我得出的结论是,我从未使用>>过可能为负的左操作数.
正如本回答中所述,算术移位甚至是在C++中定义的实现,因此与Java相比 - 在C++中甚至没有用于执行算术移位的标准化运算符.答案还说明了一个有趣的问题,我甚至都没有意识到负面数字的变化:
+63 >> 1 = +31 (integral part of quotient E1/2E2)
00111111 >> 1 = 00011111
-63 >> 1 = -32
11000001 >> 1 = 11100000
Run Code Online (Sandbox Code Playgroud)
因此,当看到这些位时,-63>>1收益率-32是显而易见的,但也许不是大多数程序员一见钟情的预期.更令人惊讶的是(但在查看位时再次显而易见)-1>>1是-1,不是0.
那么,可能负值算术右移的具体用例是什么?
我正在使用 C++ 进行一个大学研究项目,其中包含许多模板,这些模板具有进一步的嵌套模板等。该项目是关于特定研究领域的高效索引数据结构。你可以想象:一个索引结构有很多参数需要调整,所以我们过度使用模板参数。当然,我们想用不同的参数集来测试我们的索引,所以有相当多的模板实例。
这个项目并没有那么大。也许 50k LOC。但是,链接仍然需要 50 秒并消耗超过 7 GB 的内存 (!!!)。我在一个 32GB 的工作站上,所以对我来说一切都很好。我经常有学士和硕士学生在做这个项目。问题是它们通常在具有 4 GB 或 8 GB RAM 的笔记本电脑上工作。因此,这些学生在编译该项目时遇到了很大的麻烦。生成的测试二进制文件(即只包含索引结构单元测试的二进制文件)为 700 兆字节。其中大部分是符号,因为嵌套模板会产生巨大的名称。如果我strip在二进制文件上使用,它会降到 8 兆字节。
那么有没有办法减少链接过程中的RAM使用量?即使使用嵌套模板,有没有办法让符号更小?
我们std=c++11在 Ubuntu 14.10 下使用 g++4.9 进行编译。
编辑:
它似乎真的是嵌套模板。我们有两个带有非常深的嵌套模板的测试用例。.o这些测试的两个文件几乎占最终二进制文件内存的 90%。它们导致方法名称超过 3000 个字符。这里没有办法不使用嵌套模板,因为它们形成了示例查询的“处理树”。使用深度嵌套的模板时,有什么方法可以使名称保持简短?