C++的隐藏特性是什么?

114

在讨论“隐藏功能”问题时,没有关于C++的爱吗?我想我应该提出这个问题。C++有哪些隐藏功能?


@Devtron - 我曾见过一些被当作功能出售的神奇漏洞(如意外行为)。事实上,游戏行业现在正在尝试让这种情况发生,并称之为“自发游戏”(此外,请查看《灵能追击》中的“TK冲浪”,它最初只是一个漏洞,但他们将其保留并成为了游戏中最好的功能之一,在我看来)。 - Grant Peters
5
并不是很多人从头到尾读过786页的ISO C++标准,但我想你肯定做到了,并且还记住了全部内容,对吗? - j_random_hacker
2
@Laith,@j_random:请查看我的问题“程序员的笑话是什么,我如何识别它,以及适当的回应”在http://stackoverflow.com/questions/1/you-have-been-link-rolled。 - Roger Pate
请查看 http://meta.stackexchange.com/questions/56669/should-hidden-features-of-x-be-removed-closed-locked,http://meta.stackexchange.com/questions/57226/should-we-have-a-list-of-x-close-reason 以及相关的 Meta 帖子。 - Roger Pate
64个回答

9

我发现这个博客是一个关于C++奥秘的惊人资源:C++ Truths


8

本地类很棒:

struct MyAwesomeAbstractClass
{ ... };


template <typename T>
MyAwesomeAbstractClass*
create_awesome(T param)
{
    struct ans : MyAwesomeAbstractClass
    {
        // Make the implementation depend on T
    };

    return new ans(...);
}

非常整洁,因为它不会使用无用的类定义污染命名空间...


8
一个危险的秘密是:
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
}

14
抱歉,我需要您提供原始文本以便进行翻译。 - Nick Bedford
我工作的首席程序员并不知道这件事。只有一个我见过的人知道它。我还看过一些教程,其中包括我最喜欢的教程http://www.cplusplus.com/doc/tutorial/,也没有提到它。 - user34537

7

即便是对于GCC开发人员来说都不为人知的一个隐藏功能是可以使用字符串字面量来初始化数组成员。假设您有一个需要使用C数组进行工作的结构体,并且您想将数组成员初始化为默认内容。

struct Person {
  char name[255];
  Person():name("???") { }
};

这个方法只适用于字符数组和字符串字面值初始化,不需要使用 strcpy 方法!


也许只是我个人的看法,但是鉴于 void foo(){char p[255] = "daddy";} 也是初始化数组的一种合法方式,这似乎很明显。 - P Daddy
@PDaddy,这不是太明显,因为虽然在代码中经常使用="foo",但几乎从未用于成员初始化。在我读标准并发现它被允许并与其他编译器一起工作之前,我从未见过它。 - Johannes Schaub - litb
不想争论,但作为一个初学者,在尝试类似于class C{char p[255] = "daddy";};这样的东西后,下一个想到的就是“只有静态常量整型数据成员才能用类初始化”。但当时,我刚刚学习了初始化列表,所以可能更容易理解。无论如何,这绝对是一个清晰的例子,说明初始化列表与构造函数体内的赋值是不同的。 - P Daddy

7

原始类型有构造函数。

int i(3);

工作正常。


6
那不是一个构造函数,而只是一种初始化形式,即直接初始化。原始类型没有构造函数。 - GManNickG
@Midas:它使用值“3”初始化了“i”。这种语法允许与用户定义类型保持一致,这在模板中特别有用。 - André Caron

6
一个例子是:模板元编程。标准委员会中没有人打算创建一个在编译时执行的图灵完备的子语言。

模板元编程并不是一个隐蔽的特性,甚至在boost库中都有。请参阅MPL。但如果“几乎隐藏”的足够好,那么可以看看boost libraries。它包含许多好东西,但如果没有强大的库支持,这些好东西并不容易访问。

一个例子是boost.lambda库,这很有趣,因为C++当前标准中没有lambda函数。

另一个例子是Loki,它“广泛使用C++模板元编程,并实现了几个常用工具:类型列表、函数对象、单例、智能指针、对象工厂、访问者和多方法。”[维基百科]

3
模板元编程不再是隐藏的,因为它非常有用。然而,这个特性并没有被设计成C++的一部分,而是通过巧合被发现。 - Konrad Rudolph

5

没有隐藏的功能,但C++语言非常强大,甚至连标准的开发人员也无法想象C++可以用于什么。

实际上,从足够简单的语言结构中,您可以编写一些非常强大的东西。许多这样的内容可以在www.boost.org上作为示例找到(其中包括http://www.boost.org/doc/libs/1_36_0/doc/html/lambda.html)。

要理解如何将简单的语言结构组合成强大的东西,最好阅读David Vandevoorde和Nicolai M. Josuttis的《C++ Templates: The Complete Guide》以及真正神奇的书《Modern C++ Design ... 》by Andrei Alexandrescu

最后,学习C++很困难,您应该尝试填补它;)


4

我觉得只有少数人知道未命名命名空间:

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]


不幸的是,仅使用匿名命名空间仍会在最终链接中留下外部(虽然无法引用)符号。在这种情况下,如果您关心最终二进制文件大小,最好将任何文件本地函数和变量声明为静态。 - Jon
@Jon:在你剥离可执行文件之后有什么区别? - Roger Pate


3

来自C++ Truths

在同一作用域内定义具有相同签名的函数是合法的:

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";
}

3
虽然你本意是在赞成“晦涩难懂”的编程风格,但你却忽略了上面的代码需要两个额外的函数模板声明(一个在开头,一个在中间),才能编译通过。 - j_random_hacker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接