C ++的隐藏功能?
当涉及到"隐藏特性"问题时,没有C ++的热爱吗?想通了,我会把它扔在那里。 C ++的一些隐藏功能是什么?
解决方案
回答
我不确定隐藏的内容,但是有些有趣的"技巧"可能只是从阅读规范中就不明显了。
回答
C++ is a standard, there shouldn't be any hidden features...
C ++是一种多范式语言,我们可以将最后的钱押在隐藏的功能上。众多示例之一:模板元编程。标准委员会中没有人希望在编译时执行图灵完备的子语言。
回答
我发现此博客是有关C ++的奥秘的惊人资源:C ++真相。
回答
没有隐藏的功能,但是C ++语言非常强大,即使标准的开发人员也经常无法想象C ++可以用来做什么。
实际上,通过足够简单的语言构造,我们可以编写非常强大的内容。
可以在www.boost.org上获得许多此类示例作为示例(其中包括http://www.boost.org/doc/libs/1_36_0/doc/html/lambda.html)。
要了解如何将简单的语言构造与强大的功能组合在一起,最好阅读David Vandevoorde,Nicolai M. Josuttis撰写的" C ++模板:完整指南"和Andrei Alexandrescu撰写的真正神奇的书《 Modern C ++ Design ... 。
最后,学习C ++非常困难,我们应该尝试填充它;)
回答
有很多"未定义的行为"。我们可以学习如何避免他们阅读好书和阅读标准。
回答
C ++中有大量"棘手的"构造。
它们来自使用虚拟继承的密封/最终类的"简单"实现。
并获得漂亮的"复杂"元编程结构,例如Boost的MPL(教程)。用脚射击自己的可能性是无限的,但是如果加以控制(即经验丰富的程序员),则在可维护性和性能方面将提供一些最佳的灵活性。
回答
One example out of many: template metaprogramming. Nobody in the standards committee intended there to be a Turing-complete sublanguage that gets executed at compile-time.
模板元编程几乎不是隐藏的功能。它甚至在boost库中。参见MPL。但是,如果"几乎隐藏"已经足够好了,那么看看增强库。如果没有强大的库支持,它包含许多不容易获得的东西。
一个示例是boost.lambda库,这很有趣,因为C ++在当前标准中没有lambda函数。
另一个例子是Loki,它"广泛使用C ++模板元编程,并实现了几种常用工具:类型列表,函子,单例,智能指针,对象工厂,访问者和多重方法。" [维基百科]
回答
绑定到const引用的临时文件的生命周期是很少有人知道的。或者至少这是大多数人都不知道的我最喜欢的C ++知识。
const MyClass& x = MyClass(); // temporary exists as long as x is in scope
回答
数组运算符是关联的。
A [8]是*(A + 8)的同义词。由于加法是关联的,因此可以重写为*(8 + A),这是..... 8 [A]的同义词
你没说有用... :-)
回答
大多数C ++开发人员都忽略了模板元编程的功能。查看Loki Libary。它广泛使用模板元编程(来自维基百科),实现了多个高级工具,如类型列表,函子,单例,智能指针,对象工厂,访问者和多重方法。
在大多数情况下,我们可以将它们视为"隐藏的" c ++功能。
回答
我同意那里的大多数文章:C ++是一种多范式语言,因此,我们会发现"隐藏"功能(除了应不惜一切代价避免的"未定义行为")是对设施的巧妙使用。
这些功能大多数都不是语言的内置功能,而是基于库的功能。
最重要的是RAII,多年来,它经常被来自C世界的C ++开发人员所忽略。运算符重载通常是一种容易被误解的功能,它可以启用类似数组的行为(下标运算符),类似指针的操作(智能指针)和类似内置操作(乘法矩阵)。
异常的使用通常很困难,但是通过一些工作,可以通过异常安全性规范生成真正强大的代码(包括不会失败的代码,或者具有类似提交功能的代码,这些代码将成功或者恢复为正常版本)其原始状态)。
C ++最著名的"隐藏"功能是模板元编程,因为它使我们可以在编译时而不是运行时部分(或者全部)执行程序。但是,这很困难,在尝试模板之前,我们必须对模板有扎实的了解。
其他人利用多重范式在C ++的祖先(即C)之外产生"编程方式"。
通过使用函子,我们可以模拟具有添加类型安全性和状态状态的函数。使用命令模式,可以延迟代码执行。可以使用C ++轻松,有效地实现大多数其他设计模式,以产生替代的编码样式,而这些样式不应包含在"官方C ++范例"列表中。
通过使用模板,我们可以生成适用于大多数类型的代码,其中包括我们一开始没有想到的代码。我们也可以增加类型安全性(例如自动类型安全的malloc / realloc / free)。 C ++对象功能确实非常强大(因此,如果不小心使用它会很危险),但是即使动态多态性在C ++中也具有其静态版本:CRTP。
我发现,斯科特·迈耶斯(Scott Meyers)的大多数"有效C ++"类型的书或者赫伯·萨特(Herb Sutter)的"异常C ++"类型的书都易于阅读,并且非常了解C ++已知功能和鲜为人知的功能。
在我的首选中,应该使任何Java程序员都惊骇的方式:在C ++中,向对象添加功能的最面向对象的方法是通过非成员非朋友函数,而不是成员-函数(即类方法),因为:
- 在C ++中,类的接口是同一名称空间中的成员函数和非成员函数
- 非朋友非成员函数没有对类内部的特权访问。这样,在非成员非朋友成员上使用成员函数将削弱类的封装。
即使是经验丰富的开发人员,也都不会感到惊讶。
(来源:Herb Sutter的在线本周大师84#:http://www.gotw.ca/gotw/084.htm)
回答
噢,我可以拿出一份宠物恨清单:
- 如果我们打算多态使用析构函数,则它们必须是虚拟的
- 有时成员默认情况下被初始化,有时不是
- 本地分类不能用作模板参数(这使得它们不太有用)
- 异常说明符:看起来很有用,但没有用
- 函数重载隐藏具有不同签名的基类函数。
- 没有关于国际化的有用标准化(便携式标准宽字符集,有人吗?我们将不得不等到C ++ 0x)
从积极的一面
- 隐藏功能:功能尝试块。不幸的是我还没有找到用处。是的,我知道他们为什么添加它,但是我们必须重新添加一个使其毫无意义的构造函数。
- 值得一看的是修改容器后,迭代器有效性的STL保证,它可以让我们做一些更好的循环。
- 增强-几乎不是秘密,但值得使用。
- 返回值优化(不明显,但标准明确允许)
- 函子又名函数对象又名operator()。 STL广泛使用它。这并不是一个秘密,但它是操作员重载和模板的一个令人讨厌的副作用。
回答
我认为在某种程度上是隐藏的一种语言功能是名称空间别名,这是因为我在学校的整个过程中从未听说过它。直到我在boost文档中遇到它的示例时,才引起我注意。当然,现在我知道了,我们可以在任何标准C ++参考中找到它。
namespace fs = boost::filesystem; fs::path myPath( strPath, fs::native );
回答
- 指向类方法的指针
- "类型名称"关键字
回答
我们可以将URI正确地放入C ++源代码中。例如:
void foo() { http://stackoverflow.com/ int bar = 4; ... }
回答
指针算术。
它实际上是C的功能,但我注意到很少有人使用C / C ++确实知道它甚至存在。我认为C语言的这一功能真正显示了其发明家的才华和远见。
长话短说,指针算术允许编译器对任何类型的a都将a [n]作为*(a + n)执行。附带说明一下,由于'+'是可交换的,因此a [n]当然等于n [a]。
回答
摆脱前向声明:
struct global { void main() { a = 1; b(); } int a; void b(){} } singleton;
用?:运算符编写开关语句:
string result = a==0 ? "zero" : a==1 ? "one" : a==2 ? "two" : 0;
一行完成所有操作:
void a(); int b(); float c = (a(),b(),1.0f);
没有memset的调零结构:
FStruct s = {0};
归一化/包装角度和时间值:
int angle = (short)((+180+30)*65536/360) * 360/65536; //==-150
分配参考:
struct ref { int& r; ref(int& r):r(r){} }; int b; ref a(b); int c; *(int**)&a = &c;
回答
功能范围广泛的try-catch块是一个不经常使用的好功能:
int Function() try { // do something here return 42; } catch(...) { return -1; }
主要用法是将异常转换为其他异常类并重新抛出,或者在异常和基于返回的错误代码处理之间进行转换。
回答
隐藏功能:
- 纯虚函数可以实现。常见示例是纯虚拟析构函数。
- 如果一个函数抛出一个异常未在其异常规范中列出,但该函数在其异常规范中具有
std :: bad_exception
,则该异常将转换为std :: bad_exception
并自动抛出。这样,我们至少会知道抛出了" bad_exception"。在这里阅读更多。 - 功能尝试块
- template关键字用于消除类模板中的typedef的歧义。如果成员模板专业化的名称出现在
.
,->
或者::
运算符之后,并且该名称具有明确限定的模板参数,请在成员模板名称前加上关键字template。在这里阅读更多。 - 功能参数的默认值可以在运行时更改。在这里阅读更多。
A [i]
和i [A]
一样好- 一个类的临时实例可以修改!可以在临时对象上调用非const成员函数。例如:
struct Bar { void modify() {} } int main (void) { Bar().modify(); /* non-const function invoked on a temporary. */ }
在这里阅读更多。
- 如果三元(
::)运算符表达式中
:`之前和之后存在两种不同的类型,则表达式的结果类型是两者中最通用的一种。例如:
void foo (int) {} void foo (double) {} struct X { X (double d = 0.0) {} }; void foo (X) {} int main(void) { int i = 1; foo(i ? 0 : 0.0); // calls foo(double) X x; foo(i ? 0.0 : x); // calls foo(X) }
回答
构造函数中的数组初始化。
例如,在一个类中,如果我们有一个int
数组,则为:
class clName { clName(); int a[10]; };
我们可以在构造函数中将数组中的所有元素初始化为其默认值(此处,数组中的所有元素均设为零):
clName::clName() : a() { }
回答
如果缺少键,则map :: operator []会创建条目,并返回对默认构造的条目值的引用。所以你可以这样写:
map<int, string> m; string& s = m[42]; // no need for map::find() if (s.empty()) { // assuming we never store empty values in m s.assign(...); } cout << s;
令我惊讶的是,有多少C ++程序员不知道这一点。
回答
将函数或者变量放在无名的命名空间中,不赞成使用" static"将它们限制在文件范围内。
回答
大多数C ++程序员都熟悉三元运算符:
x = (y < 0) ? 10 : 20;
但是,他们没有意识到它可以用作左值:
(a == 0 ? a : b) = 1;
这是简写
if (a == 0) a = 1; else b = 1;
谨慎使用:-)
回答
将文件读入字符串向量:
vector<string> V; copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(V));
istream_iterator
回答
Pointer arithmetics.
由于可能会引入错误,因此C ++程序员更喜欢避免使用指针。
我见过最酷的C ++吗?模拟文字。
回答
一个相当隐蔽的功能是我们可以在if条件中定义变量,并且它的范围将仅覆盖if和else块:
if(int * p = getPointer()) { // do something }
一些宏使用该宏,例如,提供一些"锁定"范围,如下所示:
struct MutexLocker { MutexLocker(Mutex&); ~MutexLocker(); operator bool() const { return false; } private: Mutex &m; }; #define locked(mutex) if(MutexLocker const& lock = MutexLocker(mutex)) {} else void someCriticalPath() { locked(myLocker) { /* ... */ } }
BOOST_FOREACH也在后台使用它。要做到这一点,不仅可以在if中,而且可以在开关中:
switch(int value = getIt()) { // ... }
并在while循环中:
while(SomeThing t = getSomeThing()) { // ... }
(也处于for条件)。但是我不太确定这些是否有用:)
回答
来自C ++真相。
在同一范围内定义具有相同签名的函数,因此这是合法的:
template<class T> // (a) a base template void f(T) { std::cout << "f(T)\n"; } template<> void f<>(int*) { // (b) an explicit specialization std::cout << "f(int *) specilization\n"; } template<class T> // (c) another, overloads (a) void f(T*) { std::cout << "f(T *)\n"; } template<> void f<>(int*) { // (d) another identical explicit specialization std::cout << "f(int *) another specilization\n"; }
回答
如果运算符delete()除* void外还接受size参数,则意味着它在很大程度上将是基类。使用size参数可以检查类型的大小,以销毁正确的类型。这是斯蒂芬·德赫斯特(Stephen Dewhurst)所说的:
Notice also that we've employed a two-argument version of operator delete rather than the usual one-argument version. This two-argument version is another "usual" version of member operator delete often employed by base classes that expect derived classes to inherit their operator delete implementation. The second argument will contain the size of the object being deleted—information that is often useful in implementing custom memory management.
回答
任何编程语言中最有趣的语法之一。
其中三件事是在一起的,两件事是完全不同的...
SomeType t = u; SomeType t(u); SomeType t(); SomeType t; SomeType t(SomeType(u));
除了第三个和第五个以外的所有对象都在堆栈上定义了一个" SomeType"对象并对其进行初始化(前两种情况中使用" u",第四种情况中使用默认构造函数。第三个是声明一个不带参数的函数并返回第五,类似地声明一个函数,该函数按名为" u"的类型" SomeType"的值接受一个参数。
回答
鲜为人知的一件事是,联合也可以是模板:
template<typename From, typename To> union union_cast { From from; To to; union_cast(From from) :from(from) { } To getTo() const { return to; } };
他们也可以具有构造函数和成员函数。与继承(包括虚拟函数)无关。
回答
在类模板中定义普通朋友功能需要特别注意:
template <typename T> class Creator { friend void appear() { // a new function ::appear(), but it doesn't … // exist until Creator is instantiated } }; Creator<void> miracle; // ::appear() is created at this point Creator<double> oops; // ERROR: ::appear() is created a second time!
在此示例中,两个不同的实例创建了两个相同的定义,直接违反了ODR
因此,我们必须确保类模板的模板参数出现在该模板中定义的任何好友函数的类型中(除非我们要防止在特定文件中对一个类模板进行多个实例化,但这是不太可能的)。让我们将其应用于前面示例的变体:
template <typename T> class Creator { friend void feed(Creator<T>*){ // every T generates a different … // function ::feed() } }; Creator<void> one; // generates ::feed(Creator<void>*) Creator<double> two; // generates ::feed(Creator<double>*)
免责声明:我已经从C ++模板粘贴了本节:完整指南/第8.4节
回答
一个危险的秘密是
Fred* f = new(ram) Fred(); http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.10 f->~Fred();
我很少见到的我最喜欢的秘密:
class A { }; struct B { A a; operator A&() { return a; } }; void func(A a) { } int main() { A a, c; B b; a=c; func(b); //yeah baby a=b; //gotta love this }
回答
class Empty {}; namespace std { // #1 specializing from std namespace is okay under certain circumstances template<> void swap<Empty>(Empty&, Empty&) {} } /* #2 The following function has no arguments. There is no 'unknown argument list' as we do in C. */ void my_function() { cout << "whoa! an error\n"; // #3 using can be scoped, as it is in main below // and this doesn't affect things outside of that scope } int main() { using namespace std; /* #4 you can use using in function scopes */ cout << sizeof(Empty) << "\n"; /* #5 sizeof(Empty) is never 0 */ /* #6 falling off of main without an explicit return means "return 0;" */ }
回答
间接转换成语:
Suppose you're designing a smart pointer class. In addition to overloading the operators * and ->, a smart pointer class usually defines a conversion operator to bool:
template <class T> class Ptr { public: operator bool() const { return (rawptr ? true: false); } //..more stuff private: T * rawptr; };
The conversion to bool enables clients to use smart pointers in expressions that require bool operands:
Ptr<int> ptr(new int); if(ptr ) //calls operator bool() cout<<"int value is: "<<*ptr <<endl; else cout<<"empty"<<endl;
Furthermore, the implicit conversion to bool is required in conditional declarations such as:
if (shared_ptr<X> px = dynamic_pointer_cast<X>(py)) { //we get here only of px isn't empty }
Alas, this automatic conversion opens the gate to unwelcome surprises:
Ptr <int> p1; Ptr <double> p2; //surprise #1 cout<<"p1 + p2 = "<< p1+p2 <<endl; //prints 0, 1, or 2, although there isn't an overloaded operator+() Ptr <File> pf; Ptr <Query> pq; // Query and File are unrelated //surprise #2 if(pf==pq) //compares bool values, not pointers!
解决方案:使用"间接转换"惯用语,即从指针到数据成员[pMember]的指针到bool的转换,这样将只有1个隐式转换,这将防止上述意外行为:pMember-> bool而不是bool-> something别的。
回答
注意自由函数指针和成员函数指针初始化之间的区别:
成员函数:
struct S { void func(){}; }; int main(){ void (S::*pmf)()=&S::func;// & is mandatory }
和免费功能:
void func(int){} int main(){ void (*pf)(int)=func; // & is unnecessary it can be &func as well; }
多亏了这个多余的&,我们可以添加流操纵器-流操纵器是链中的免费函数,没有它:
cout<<hex<<56; //otherwise you would have to write cout<<&hex<<56, not neat.
回答
C ++模板是否可以检查功能是否存在?
回答
用静态转换模拟重新解释转换:
int var; string *str = reinterpret_cast<string*>(&var);
上面的代码等效于以下代码:
int var; string *str = static_cast<string*>(static_cast<void*>(&var));
回答
class和struct class-keys几乎相同。主要区别在于,类默认为成员和基础的私有访问权,而结构默认为公共的私有访问权:
// this is completely valid C++: class A; struct A { virtual ~A() = 0; }; class B : public A { public: virtual ~B(); }; // means the exact same as: struct A; class A { public: virtual ~A() = 0; }; struct B : A { virtual ~B(); }; // you can't even tell the difference from other code whether 'struct' // or 'class' was used for A and B
联合还可以具有成员和方法,并且与结构类似,默认为公共访问。
回答
不仅可以在for循环的init部分中声明变量,还可以在类和函数中声明变量。
for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) { ... }
这允许使用不同类型的多个变量。
回答
向模板添加约束。
回答
基本类型具有构造函数。
int i(3);
作品。
回答
在我看来,只有少数人知道未命名的名称空间:
namespace { // Classes, functions, and objects here. }
未命名的命名空间的行为就像被以下内容取代:
namespace __unique_name__ { /* empty body */ } using namespace __unique_name__; namespace __unique_name__ { // original namespace body }
" ..翻译单元中所有出现的[此唯一名称]都被相同的标识符替换,并且该标识符与整个程序中的所有其他标识符不同。 [C ++ 03,7.3.1.1/1]
回答
我们可以访问任何类的受保护数据和函数成员,而没有未定义的行为,并且可以使用预期的语义。继续阅读以了解操作方法。另请阅读有关此问题的缺陷报告。
通常,C ++禁止我们访问类对象的非静态受保护成员,即使该类是基类也是如此。
struct A { protected: int a; }; struct B : A { // error: can't access protected member static int get(A &x) { return x.a; } }; struct C : A { };
那是被禁止的:我们和编译器不知道引用实际指向的内容。它可能是一个" C"对象,在这种情况下,类" B"没有任何业务和关于其数据的线索。仅当" x"是对派生类的引用或者从其派生的类的引用时,才授予此类访问权限。它可以通过组成一个"抛出"类来读取成员的方式,允许任意一段代码读取受保护的成员,例如std :: stack
:
void f(std::stack<int> &s) { // now, let's decide to mess with that stack! struct pillager : std::stack<int> { static std::deque<int> &get(std::stack<int> &s) { // error: stack<int>::c is protected return s.c; } }; // haha, now let's inspect the stack's middle elements! std::deque<int> &d = pillager::get(s); }
当然,如我们所见,这会造成太大的损坏。但是现在,成员指针允许规避此保护!关键是成员指针的类型绑定到实际包含该成员的类,而不绑定到获取地址时指定的类。这使我们可以规避检查
struct A { protected: int a; }; struct B : A { // valid: *can* access protected member static int get(A &x) { return x.*(&B::a); } }; struct C : A { };
当然,它也可以与std :: stack
示例一起使用。
void f(std::stack<int> &s) { // now, let's decide to mess with that stack! struct pillager : std::stack<int> { static std::deque<int> &get(std::stack<int> &s) { return s.*(pillager::c); } }; // haha, now let's inspect the stack's middle elements! std::deque<int> &d = pillager::get(s); }
通过在派生类中使用using声明,这将变得更加容易,这将使成员名称成为公共名称并引用基类的成员。
void f(std::stack<int> &s) { // now, let's decide to mess with that stack! struct pillager : std::stack<int> { using std::stack<int>::c; }; // haha, now let's inspect the stack's middle elements! std::deque<int> &d = s.*(&pillager::c); }
回答
成员指针和成员指针运算符-> *
#include <stdio.h> struct A { int d; int e() { return d; } }; int main() { A* a = new A(); a->d = 8; printf("%d %d\n", a ->* &A::d, (a ->* &A::e)() ); return 0; }
对于方法(a-> *&A :: e)()有点像javascript中的Function.call()
var f = A.e f.call(a)
对于成员来说,有点像使用[]运算符进行访问
a['d']
回答
许多人都知道identity
/id
元函数,但是对于非模板的情况有一个很好的用例:易于编写声明:
// void (*f)(); // same id<void()>::type *f; // void (*f(void(*p)()))(int); // same id<void(int)>::type *f(id<void()>::type *p); // int (*p)[2] = new int[10][2]; // same id<int[2]>::type *p = new int[10][2]; // void (C::*p)(int) = 0; // same id<void(int)>::type C::*p = 0;
它极大地帮助解密C ++声明!
// boost::identity is pretty much the same template<typename T> struct id { typedef T type; };
回答
抛出是一个表达
回答
我发现递归模板声明非常酷:
template<class int> class foo; template class foo<0> { int* get<0>() { return array; } int* array; }; template<class int> class foo<i> : public foo<i-1> { int* get<i>() { return array + 1; } };
我使用它来生成具有10-15个函数的类,这些类将指针返回到数组的各个部分,因为我使用的API每个值都需要一个函数指针。
IE。对编译器进行编程,以通过递归生成一堆函数。非常简单。 :)
回答
main()不需要返回值:
int main(){}
是最短的有效C ++程序。
回答
我们可以模板化位域。
template <size_t X, size_t Y> struct bitfield { char left : X; char right : Y; };
我还没有想出什么目的,但是确实令我惊讶。
回答
我目前最喜欢的是在类似以下语句中缺少语义
A = B = C。 A的值基本上是不确定的。
想一想:
class clC { public: clC& operator=(const clC& other) { //do some assignment stuff return copy(other); } virtual clC& copy(const clC& other); } class clB : public clC { public: clB() : m_copy() { } clC& copy(const clC& other) { return m_copy; } private: class clInnerB : public clC { } clInnerB m_copy; }
现在,A可能是除clB类型的对象之外其他任何对象都无法访问的类型,并且其值与C无关。
回答
void函数可以返回void值
鲜为人知,但以下代码可以
void f() { } void g() { return f(); }
以及以下怪异的看一个
void f() { return (void)"i'm discarded"; }
知道了这一点,我们可以在某些方面加以利用。一个例子:void
函数不能返回值,但我们也不能不返回任何值,因为它们可以用non-void实例化。与其将值存储到局部变量(这会导致void
的错误),不如直接返回一个值
template<typename T> struct sample { // assume f<T> may return void T dosomething() { return f<T>(); } // better than T t = f<T>(); /* ... */ return t; ! };
回答
防止逗号运算符调用运算符重载
有时我们可以有效地使用逗号运算符,但是我们要确保没有用户定义的逗号运算符进入该方式,因为例如我们依赖于左侧和右侧之间的序列点,或者想确保没有任何干扰点行动。这是void()
进入游戏的地方:
for(T i, j; can_continue(i, j); ++i, void(), ++j) do_code(i, j);
忽略我输入条件和代码的占位符。重要的是void()
,它使编译器强制使用内置的逗号运算符。有时也可以在实现特征类时使用。
回答
我们可以使用某些编译器通过命令行开关查看所有预定义的宏。这适用于gcc和icc(Intel的C ++编译器):
$ touch empty.cpp $ g++ -E -dM empty.cpp | sort >gxx-macros.txt $ icc -E -dM empty.cpp | sort >icx-macros.txt $ touch empty.c $ gcc -E -dM empty.c | sort >gcc-macros.txt $ icc -E -dM empty.c | sort >icc-macros.txt
对于MSVC,它们在单个位置列出。也可以将它们记录在其他地方的单个位置中,但是在应用所有其他命令行开关之后,使用上述命令,我们可以清楚地看到定义了什么,未定义什么以及使用了正确的值。
比较(排序后):
$ diff gxx-macros.txt icx-macros.txt $ diff gxx-macros.txt gcc-macros.txt $ diff icx-macros.txt icc-macros.txt
回答
三元条件运算符?:
要求其第二和第三个操作数具有"令人满意的"类型(非正式地说)。但是这一要求有一个例外(双关语意味):第二个或者第三个操作数可以是throw表达式(类型为" void"),而与另一个操作数的类型无关。
换句话说,可以使用?:
运算符编写以下正确有效的C ++表达式。
i = a > b ? a : throw something();
顺便说一句,throw表达式实际上是表达式(" void"类型)而不是语句,这是C ++语言的另一个鲜为人知的功能。这意味着,除其他外,以下代码是完全有效的
void foo() { return throw something(); }
尽管这样做没有什么意义(也许在某些通用模板代码中可能会派上用场)。
回答
- map :: insert(std :: pair(key,value));`如果键值已经存在,则不会覆盖。
- 我们可以在定义后立即实例化一个类:(我可能会补充说,由于缺少分号,此功能使我产生了数百个编译错误,而且我从未见过有人在类上使用它)
class MyClass {public: /* code */} myClass;
回答
我知道有人只用一种方法同时定义了一个getter和setter。像这样:
class foo { int x; int* GetX(){ return &x; } }
现在,我们可以像往常一样(差不多)将其用作吸气剂:
int a = *GetX();
并作为二传手:
*GetX() = 17;
回答
模板元编程是。
回答
实际上不是隐藏的功能,而是纯粹的出色功能:
#define private public
回答
我们可以将变量引用作为函数的一部分返回。它有一些用途,主要用于产生可怕的代码:
int s ; vector <int> a ; vector <int> b ; int &G(int h) { if ( h < a.size() ) return a[h] ; if ( h - a.size() < b.size() ) return b[ h - a.size() ] ; return s ; } int main() { a = vector <int> (100) ; b = vector <int> (100) ; G( 20) = 40 ; //a[20] becomes 40 G(120) = 40 ; //b[20] becomes 40 G(424) = 40 ; //s becomes 40 }
回答
本地课程很棒:
struct MyAwesomeAbstractClass { ... }; template <typename T> MyAwesomeAbstractClass* create_awesome(T param) { struct ans : MyAwesomeAbstractClass { // Make the implementation depend on T }; return new ans(...); }
非常整洁,因为它不会用无用的类定义来污染名称空间...
回答
一个隐藏的功能(甚至是GCC开发人员也隐藏的功能)是使用字符串文字初始化数组成员。假设我们有一个需要使用C数组的结构,并且想要使用默认内容初始化该数组成员
struct Person { char name[255]; Person():name("???") { } };
这有效,并且仅适用于char数组和字符串文字初始值设定项。不需要strcpy
!
回答
另一个隐藏的功能是,我们可以调用可以转换为函数指针或者引用的类对象。对它们的结果进行重载解析,并完美地转发参数。
template<typename Func1, typename Func2> class callable { Func1 *m_f1; Func2 *m_f2; public: callable(Func1 *f1, Func2 *f2):m_f1(f1), m_f2(f2) { } operator Func1*() { return m_f1; } operator Func2*() { return m_f2; } }; void foo(int i) { std::cout << "foo: " << i << std::endl; } void bar(long il) { std::cout << "bar: " << il << std::endl; } int main() { callable<void(int), void(long)> c(foo, bar); c(42); // calls foo c(42L); // calls bar }
这些被称为"代理调用函数"。
回答
在C中不起作用的另一个隐藏功能是一元运算符+的功能。我们可以使用它来促进和衰减各种事物
将枚举转换为整数
+AnEnumeratorValue
我们以前具有其枚举类型的枚举器值现在具有可以适合其值的完美整数类型。手动地,我们几乎不知道该类型!例如,当我们要为枚举实现重载运算符时,就需要使用此方法。
从变量中获取值
我们必须使用一个使用类内静态初始化程序的类,而没有类外定义,但是有时它无法链接?运算符可以帮助创建临时文件,而无需对其类型进行任何假设或者依赖
struct Foo { static int const value = 42; }; // This does something interesting... template<typename T> void f(T const&); int main() { // fails to link - tries to get the address of "Foo::value"! f(Foo::value); // works - pass a temporary value f(+Foo::value); }
衰减指向指针的数组
我们是否想将两个指针传递给一个函数,但是它根本行不通?接线员可能会帮助
// This does something interesting... template<typename T> void f(T const& a, T const& b); int main() { int a[2]; int b[3]; f(a, b); // won't work! different values for "T"! f(+a, +b); // works! T is "int*" both time }
回答
统治规则很有用,但鲜为人知。它说,即使在通过基类网格的非唯一路径中,如果成员属于虚拟基类,则部分隐藏的成员的名称查找也是唯一的:
struct A { void f() { } }; struct B : virtual A { void f() { cout << "B!"; } }; struct C : virtual A { }; // name-lookup sees B::f and A::f, but B::f dominates over A::f ! struct D : B, C { void g() { f(); } };
我使用它来实现对齐支持,该支持通过优势规则自动找出最严格的对齐方式。
这不仅适用于虚拟函数,而且还适用于typedef名称,静态/非虚拟成员以及其他任何东西。我已经看到它曾经在元程序中实现可重写的特征。