Không yêu C ++ khi nói đến dòng câu hỏi "tính năng ẩn của"? Tôi sẽ ném nó ra khỏi đó. Một số tính năng ẩn của C ++ là gì?
Không yêu C ++ khi nói đến dòng câu hỏi "tính năng ẩn của"? Tôi sẽ ném nó ra khỏi đó. Một số tính năng ẩn của C ++ là gì?
Câu trả lời:
Hầu hết các lập trình viên C ++ đều quen thuộc với toán tử bậc ba:
x = (y < 0) ? 10 : 20;
Tuy nhiên, họ không nhận ra rằng nó có thể được sử dụng như một lvalue:
(a == 0 ? a : b) = 1;
đó là viết tắt của
if (a == 0)
a = 1;
else
b = 1;
Sử dụng cẩn thận :-)
(value ? function1 : function2)()
.
function1
và function2
được chuyển đổi hoàn toàn thành con trỏ hàm, và kết quả hoàn toàn được chuyển đổi ngược lại.
Bạn có thể đặt URI vào mã nguồn C ++ mà không bị lỗi. Ví dụ:
void foo() {
http://stackoverflow.com/
int bar = 4;
...
}
goto
C ++). Bất cứ điều gì sau hai dấu gạch chéo là một nhận xét. Do đó, với http://stackoverflow.com
, http
là một nhãn (về mặt lý thuyết, bạn có thể viết goto http;
), và //stackoverflow.com
chỉ là một nhận xét cuối dòng. Cả hai đều là C ++ hợp pháp, vì vậy cấu trúc sẽ biên dịch. Tất nhiên, nó không làm bất cứ điều gì hữu ích một cách mơ hồ.
goto http;
nó không thực sự theo sau URL. :(
Số học con trỏ.
Các lập trình viên C ++ thích tránh con trỏ vì các lỗi có thể được đưa vào.
C ++ thú vị nhất mà tôi từng thấy? Chữ tương tự.
Tôi đồng ý với hầu hết các bài viết ở đó: C ++ là một ngôn ngữ đa mô hình, vì vậy các tính năng "ẩn" mà bạn sẽ tìm thấy (ngoài "các hành vi không xác định" mà bạn nên tránh bằng mọi giá) là cách sử dụng thông minh của các phương tiện.
Hầu hết các phương tiện đó không phải là các tính năng tích hợp của ngôn ngữ mà là các tính năng dựa trên thư viện.
Quan trọng nhất là RAII , thường bị các nhà phát triển C ++ đến từ thế giới C bỏ qua trong nhiều năm. Nạp chồng toán tử thường là một tính năng bị hiểu nhầm cho phép cả hành vi giống mảng (toán tử chỉ số con), hoạt động giống như con trỏ (con trỏ thông minh) và hoạt động giống như xây dựng (nhân ma trận).
Việc sử dụng ngoại lệ thường khó khăn, nhưng với một số công việc, có thể tạo ra mã thực sự mạnh mẽ thông qua an toàn ngoại lệ số kỹ thuật (bao gồm mã sẽ không bị lỗi hoặc sẽ có các tính năng giống như cam kết sẽ thành công hoặc hoàn nguyên trở lại trạng thái ban đầu của nó).
Tính năng "ẩn" nổi tiếng nhất của C ++ là lập trình siêu mẫu , vì nó cho phép bạn thực thi một phần (hoặc toàn bộ) chương trình của mình tại thời điểm biên dịch thay vì thời gian chạy. Tuy nhiên, điều này rất khó và bạn phải nắm chắc các mẫu trước khi thử.
Người khác sử dụng nhiều mô hình để tạo ra "cách lập trình" bên ngoài tổ tiên của C ++, tức là C.
Bằng cách sử dụng functors , bạn có thể mô phỏng chức năng, với thêm kiểu an toàn và là stateful. Sử dụng mẫu lệnh , bạn có thể trì hoãn việc thực thi mã. Hầu hết các mẫu thiết kế khác có thể được triển khai dễ dàng và hiệu quả trong C ++ để tạo ra các kiểu mã hóa thay thế không được cho là nằm trong danh sách "các mô hình C ++ chính thức".
Bằng cách sử dụng các mẫu , bạn có thể tạo ra mã sẽ hoạt động trên hầu hết các loại, kể cả loại mã mà bạn nghĩ lúc đầu. Bạn cũng có thể tăng độ an toàn của kiểu (như an toàn kiểu tự động malloc / realloc / free). Các tính năng của đối tượng C ++ thực sự mạnh mẽ (và do đó, nguy hiểm nếu sử dụng bất cẩn), nhưng ngay cả tính đa hình động cũng có phiên bản tĩnh của nó trong C ++: CRTP .
Tôi nhận thấy rằng hầu hết các sách kiểu " C ++ hiệu quả " của Scott Meyers hoặc sách kiểu " C ++ đặc biệt " từ Herb Sutter đều dễ đọc và có kho thông tin về các tính năng đã biết và ít được biết đến của C ++.
Trong số ưu tiên của tôi là một điều khiến bất kỳ lập trình viên Java nào cũng phải kinh hãi: Trong C ++, cách hướng đối tượng tốt nhất để thêm một tính năng vào một đối tượng là thông qua một hàm non-member non-friend, thay vì một member- hàm (tức là phương thức lớp), bởi vì:
Trong C ++, giao diện của một lớp 'vừa là các hàm thành viên vừa là các hàm không phải thành viên trong cùng một không gian tên
chức năng non-friend non-member không có quyền truy cập đặc quyền vào nội bộ lớp. Do đó, việc sử dụng một hàm thành viên thay cho một hàm không phải là thành viên không phải là bạn bè sẽ làm suy yếu tính đóng gói của lớp.
Điều này không bao giờ làm ngạc nhiên ngay cả những nhà phát triển có kinh nghiệm.
(Nguồn: Trong số những người khác, Guru trực tuyến của Herb Sutter của Tuần # 84: http://www.gotw.ca/gotw/084.htm )
Một đặc điểm ngôn ngữ mà tôi cho là hơi bị ẩn, vì tôi chưa bao giờ nghe nói về nó trong suốt thời gian đi học, là bí danh không gian tên. Nó không được chú ý cho đến khi tôi tìm thấy các ví dụ về nó trong tài liệu tăng cường. Tất nhiên, bây giờ tôi biết về nó, bạn có thể tìm thấy nó trong bất kỳ tài liệu tham khảo C ++ chuẩn nào.
namespace fs = boost::filesystem;
fs::path myPath( strPath, fs::native );
using
.
Các biến không chỉ có thể được khai báo trong phần init của một for
vòng lặp mà còn cả các lớp và hàm.
for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) {
...
}
Điều đó cho phép nhiều biến thuộc các kiểu khác nhau.
Toán tử mảng là liên kết.
A [8] là một từ đồng nghĩa với * (A + 8). Vì phép cộng có tính chất kết hợp, nên có thể được viết lại thành * (8 + A), là từ đồng nghĩa với ..... 8 [A]
Bạn không nói hữu ích ... :-)
A
không quan trọng ở tất cả. Ví dụ, nếu A
là a char*
, mã sẽ vẫn hợp lệ.
Một điều ít người biết là các công đoàn cũng có thể là các khuôn mẫu:
template<typename From, typename To>
union union_cast {
From from;
To to;
union_cast(From from)
:from(from) { }
To getTo() const { return to; }
};
Và chúng cũng có thể có các hàm tạo và các hàm thành viên. Chỉ không có gì liên quan đến kế thừa (bao gồm cả các chức năng ảo).
From
và To
được thiết lập và sử dụng cho phù hợp. Tuy nhiên, kết hợp như vậy có thể được sử dụng với hành vi đã xác định (với To
việc là một mảng các char không dấu hoặc một cấu trúc chia sẻ một chuỗi ban đầu với From
). Ngay cả khi bạn sử dụng nó theo cách không xác định, nó vẫn có thể hữu ích cho công việc cấp thấp. Dù sao, đây chỉ là một ví dụ về mẫu liên minh - có thể có những cách sử dụng khác cho liên minh mẫu.
C ++ là một tiêu chuẩn, không nên có bất kỳ tính năng ẩn nào ...
C ++ là một ngôn ngữ đa mô hình, bạn có thể đặt cược số tiền cuối cùng của mình vào đó là các tính năng ẩn. Một ví dụ trong số nhiều ví dụ: lập trình siêu mẫu theo mẫu . Không ai trong ủy ban tiêu chuẩn có ý định có một ngôn ngữ con hoàn chỉnh Turing được thực thi tại thời điểm biên dịch.
Một tính năng ẩn khác không hoạt động trong C là chức năng của toán +
tử một ngôi. Bạn có thể sử dụng nó để quảng bá và phân rã mọi thứ
+AnEnumeratorValue
Và giá trị kiểu liệt kê của bạn trước đây có kiểu liệt kê giờ có kiểu số nguyên hoàn hảo có thể phù hợp với giá trị của nó. Theo cách thủ công, bạn sẽ khó biết loại đó! Điều này là cần thiết, chẳng hạn khi bạn muốn triển khai toán tử nạp chồng cho kiểu liệt kê của mình.
Bạn phải sử dụng một lớp sử dụng bộ khởi tạo tĩnh trong lớp mà không có định nghĩa ngoài lớp, nhưng đôi khi nó không liên kết được? Nhà điều hành có thể giúp tạo một tạm thời mà không cần đưa ra các giả định hoặc phụ thuộc vào loại của nó
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);
}
Bạn có muốn chuyển hai con trỏ đến một hàm nhưng nó không hoạt động không? Nhà điều hành có thể giúp
// 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
}
Thời gian tồn tại của thời gian tạm thời bị ràng buộc với tham chiếu const là một trong những điều mà ít người biết đến. Hoặc ít nhất đó là phần kiến thức C ++ yêu thích của tôi mà hầu hết mọi người đều không biết.
const MyClass& x = MyClass(); // temporary exists as long as x is in scope
Một tính năng thú vị không được sử dụng thường xuyên là khối try-catch toàn hàm:
int Function()
try
{
// do something here
return 42;
}
catch(...)
{
return -1;
}
Cách sử dụng chính sẽ là dịch ngoại lệ sang lớp ngoại lệ khác và rethrow, hoặc dịch giữa các ngoại lệ và xử lý mã lỗi dựa trên trả về.
return
bắt được khối chức năng Thử, chỉ có thể phát lại.
Nhiều người biết về identity
/ id
meta Chức năng, nhưng có một công dụng tuyệt vời dành cho nó đối với các trường hợp không phải khuôn mẫu:
// 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;
Nó giúp giải mã các khai báo C ++ rất nhiều!
// boost::identity is pretty much the same
template<typename T>
struct id { typedef T type; };
template<typename Ret,typename... Args> using function = Ret (Args...); template<typename T> using pointer = *T;
-> pointer<function<void,int>> f(pointer<function<void,void>>);
hay pointer<void(int)> f(pointer<void()>);
hayfunction<pointer<function<void,int>>,pointer<function<void,void>>> f;
Một tính năng khá ẩn là bạn có thể xác định các biến trong điều kiện if và phạm vi của nó sẽ chỉ trải dài trên if và các khối khác của nó:
if(int * p = getPointer()) {
// do something
}
Ví dụ: một số macro sử dụng điều đó để cung cấp một số phạm vi "bị khóa" như sau:
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) { /* ... */ }
}
Ngoài ra BOOST_FOREACH sử dụng nó dưới mui xe. Để hoàn thành việc này, bạn không chỉ có thể thực hiện được trong if mà còn có thể thực hiện được trong switch:
switch(int value = getIt()) {
// ...
}
và trong một vòng lặp while:
while(SomeThing t = getSomeThing()) {
// ...
}
(và cả trong một điều kiện). Nhưng tôi không chắc liệu những thứ này có hữu ích hay không :)
if((a = f()) == b) ...
, nhưng câu trả lời này thực sự khai báo một biến trong điều kiện.
for(...; int i = foo(); ) ...;
Điều này sẽ đi qua phần thân miễn i
là đúng, khởi tạo lại nó mỗi lần. Vòng lặp mà bạn thấy chỉ đơn giản là thể hiện một tuyên bố thay đổi, nhưng không phải là một lời tuyên bố biến mà simultanuously đóng vai trò như một điều kiện :)
Đôi khi bạn sử dụng hợp lệ toán tử dấu phẩy, nhưng bạn muốn đảm bảo rằng không có toán tử dấu phẩy nào do người dùng xác định gây cản trở, vì ví dụ: bạn dựa vào các điểm trình tự giữa bên trái và bên phải hoặc muốn đảm bảo không có gì cản trở điều mong muốn hoạt động. Đây là nơi void()
xuất hiện trong trò chơi:
for(T i, j; can_continue(i, j); ++i, void(), ++j)
do_code(i, j);
Bỏ qua những người giữ chỗ mà tôi đặt cho điều kiện và mã. Điều quan trọng là void()
, khiến trình biên dịch buộc phải sử dụng toán tử dấu phẩy nội trang. Điều này cũng có thể hữu ích khi triển khai các lớp đặc điểm.
Khởi tạo mảng trong phương thức khởi tạo. Ví dụ trong một lớp nếu chúng ta có một mảng int
là:
class clName
{
clName();
int a[10];
};
Chúng ta có thể khởi tạo tất cả các phần tử trong mảng thành mặc định của nó (ở đây tất cả các phần tử của mảng bằng 0) trong hàm tạo như:
clName::clName() : a()
{
}
Oooh, tôi có thể đưa ra một danh sách những điều mà thú cưng ghét thay thế:
Về mặt tích cực
Bạn có thể truy cập dữ liệu được bảo vệ và các thành viên chức năng của bất kỳ lớp nào, không có hành vi không xác định và với ngữ nghĩa mong đợi. Đọc tiếp để xem làm thế nào. Đọc thêm báo cáo khiếm khuyết về điều này.
Thông thường, C ++ cấm bạn truy cập vào các thành viên được bảo vệ không tĩnh của đối tượng của lớp, ngay cả khi lớp đó là lớp cơ sở của bạn
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 { };
Điều đó bị cấm: Bạn và trình biên dịch không biết tham chiếu thực sự trỏ đến cái gì. Nó có thể là một C
đối tượng, trong trường hợp đó lớp B
không có nghiệp vụ và manh mối về dữ liệu của nó. Quyền truy cập như vậy chỉ được cấp nếu x
là một tham chiếu đến một lớp dẫn xuất hoặc một lớp dẫn xuất từ nó. Và nó có thể cho phép đoạn mã tùy ý đọc bất kỳ thành viên được bảo vệ nào bằng cách tạo một lớp "vứt bỏ" để đọc ra các thành viên, ví dụ như 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);
}
Chắc chắn, như bạn thấy điều này sẽ gây ra quá nhiều thiệt hại. Nhưng bây giờ, con trỏ thành viên cho phép phá vỡ sự bảo vệ này! Điểm mấu chốt là kiểu con trỏ thành viên được liên kết với lớp thực sự chứa thành viên đó - không phải với lớp mà bạn đã chỉ định khi lấy địa chỉ. Điều này cho phép chúng tôi tránh kiểm tra
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 { };
Và tất nhiên, nó cũng hoạt động với std::stack
ví dụ.
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);
}
Điều đó sẽ trở nên dễ dàng hơn với một khai báo sử dụng trong lớp dẫn xuất, khai báo này làm cho tên thành viên ở chế độ công khai và tham chiếu đến thành viên của lớp cơ sở.
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);
}
Một tính năng ẩn khác là bạn có thể gọi các đối tượng lớp có thể được chuyển đổi thành con trỏ hàm hoặc tham chiếu. Việc giải quyết quá tải được thực hiện dựa trên kết quả của chúng và các đối số được chuyển tiếp hoàn hảo.
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
}
Chúng được gọi là "hàm gọi thay thế".
Các tính năng ẩn:
Nếu một hàm ném ra một ngoại lệ không được liệt kê trong đặc tả ngoại lệ của nó, nhưng hàm có std::bad_exception
trong đặc tả ngoại lệ của nó, thì ngoại lệ đó sẽ được chuyển đổi thành std::bad_exception
và ném tự động. Bằng cách đó, ít nhất bạn sẽ biết rằng a bad_exception
đã được ném. Đọc thêm tại đây .
chức năng thử khối
Từ khóa mẫu trong việc phân biệt các typedef trong một mẫu lớp. Nếu tên của chuyên ngành mẫu thành viên xuất hiện sau một .
, ->
hoặc ::
toán tử và tên đó có các tham số mẫu đủ điều kiện rõ ràng, hãy thêm tiền tố tên mẫu thành viên với mẫu từ khóa. Đọc thêm tại đây .
Mặc định tham số hàm có thể được thay đổi trong thời gian chạy. Đọc thêm tại đây .
A[i]
hoạt động tốt như i[A]
Các phiên bản tạm thời của một lớp có thể được sửa đổi! Một hàm thành viên không phải const có thể được gọi trên một đối tượng tạm thời. Ví dụ:
struct Bar {
void modify() {}
}
int main (void) {
Bar().modify(); /* non-const function invoked on a temporary. */
}
Đọc thêm tại đây .
Nếu có hai kiểu khác nhau trước và sau :
trong ?:
biểu thức toán tử ternary ( ), thì kiểu kết quả của biểu thức là kiểu tổng quát nhất trong hai kiểu. Ví dụ:
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)
}
map::operator[]
tạo mục nhập nếu thiếu khóa và trả về tham chiếu đến giá trị mục nhập được tạo mặc định. Vì vậy, bạn có thể viết:
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;
Tôi ngạc nhiên về việc có bao nhiêu lập trình viên C ++ không biết điều này.
.find()
.
const map::operator[]
tạo thông báo lỗi"
Việc đặt các hàm hoặc biến trong không gian tên không tên sẽ không sử dụng static
để hạn chế chúng trong phạm vi tệp.
static
trong phạm vi toàn cầu không bị phản đối theo bất kỳ cách nào. (Tham khảo: C ++ 03 §D.2)
static
sử dụng chỉ nên được sử dụng trong một loại lớp hoặc hàm.
Việc xác định các hàm bạn bè thông thường trong các mẫu lớp cần đặc biệt chú ý:
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!
Trong ví dụ này, hai cách diễn đạt khác nhau tạo ra hai định nghĩa giống hệt nhau — vi phạm trực tiếp ODR
Do đó, chúng ta phải đảm bảo các tham số mẫu của mẫu lớp xuất hiện trong kiểu của bất kỳ hàm bạn bè nào được xác định trong mẫu đó (trừ khi chúng ta muốn ngăn nhiều hơn một lần khởi tạo mẫu lớp trong một tệp cụ thể, nhưng điều này khá khó xảy ra). Hãy áp dụng điều này cho một biến thể của ví dụ trước của chúng tôi:
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>*)
Tuyên bố từ chối trách nhiệm: Tôi đã dán phần này từ C ++ Templates: The Complete Guide / Section 8.4
Ít được biết đến, nhưng mã sau đây là tốt
void f() { }
void g() { return f(); }
Cũng như cái nhìn kỳ lạ sau đây
void f() { return (void)"i'm discarded"; }
Biết về điều này, bạn có thể tận dụng trong một số lĩnh vực. Một ví dụ: các void
hàm không thể trả về một giá trị nhưng bạn cũng không thể trả về không có giá trị nào, bởi vì chúng có thể được khởi tạo bằng non-void. Thay vì lưu trữ giá trị vào một biến cục bộ, điều này sẽ gây ra lỗi cho void
, chỉ cần trả về một giá trị trực tiếp
template<typename T>
struct sample {
// assume f<T> may return void
T dosomething() { return f<T>(); }
// better than T t = f<T>(); /* ... */ return t; !
};
Đọc tệp thành một vectơ chuỗi:
vector<string> V;
copy(istream_iterator<string>(cin), istream_iterator<string>(),
back_inserter(V));
vector<string> V((istream_iterator<string>(cin)), istream_iterator<string>());
- thiếu dấu ngoặc đơn sau thông số thứ hai
Bạn có thể tạo mẫu các trường bit.
template <size_t X, size_t Y>
struct bitfield
{
char left : X;
char right : Y;
};
Tôi vẫn chưa nghĩ ra bất kỳ mục đích nào cho việc này, nhưng chắc chắn là tôi đã rất ngạc nhiên.
Một trong những ngữ pháp thú vị nhất của bất kỳ ngôn ngữ lập trình nào.
Ba trong số những thứ này thuộc về nhau, và hai thứ hoàn toàn khác nhau ...
SomeType t = u;
SomeType t(u);
SomeType t();
SomeType t;
SomeType t(SomeType(u));
Tất cả ngoại trừ SomeType
đối tượng thứ ba và thứ năm xác định một đối tượng trên ngăn xếp và khởi tạo nó (với u
trong hai trường hợp đầu tiên và phương thức khởi tạo mặc định ở thứ tư. Câu thứ ba là khai báo một hàm không nhận tham số và trả về a SomeType
. Thứ năm cũng khai báo tương tự một hàm nhận một tham số theo giá trị của kiểu SomeType
được đặt tên u
.
Loại bỏ các khai báo chuyển tiếp:
struct global
{
void main()
{
a = 1;
b();
}
int a;
void b(){}
}
singleton;
Viết câu lệnh switch với?: Toán tử:
string result =
a==0 ? "zero" :
a==1 ? "one" :
a==2 ? "two" :
0;
Làm mọi thứ trên một dòng:
void a();
int b();
float c = (a(),b(),1.0f);
Zeroing cấu trúc không có memset:
FStruct s = {0};
Chuẩn hóa / giá trị góc - và thời gian gói:
int angle = (short)((+180+30)*65536/360) * 360/65536; //==-150
Gán tài liệu tham khảo:
struct ref
{
int& r;
ref(int& r):r(r){}
};
int b;
ref a(b);
int c;
*(int**)&a = &c;
FStruct s = {};
thậm chí còn ngắn hơn.
main
? Tôi muốn đề xuất global().main();
và chỉ cần quên về singleton ( bạn chỉ có thể làm việc với cái tạm thời, điều này sẽ kéo dài thời gian tồn tại của nó )
Toán tử điều kiện bậc ba ?:
yêu cầu toán hạng thứ hai và thứ ba của nó phải có kiểu "dễ chấp nhận" (nói một cách không chính thức). Nhưng yêu cầu này có một ngoại lệ (dự định chơi chữ): toán hạng thứ hai hoặc thứ ba có thể là một biểu thức ném (có kiểuvoid
), bất kể kiểu của toán hạng khác.
Nói cách khác, người ta có thể viết các biểu thức C ++ hợp lệ không chính xác sau đây bằng cách sử dụng ?:
toán tử
i = a > b ? a : throw something();
BTW, thực tế là biểu thức ném thực sự là một biểu thức (kiểu void
) chứ không phải là một câu lệnh là một tính năng ít được biết đến của ngôn ngữ C ++. Điều này có nghĩa là, trong số những thứ khác, mã sau hoàn toàn hợp lệ
void foo()
{
return throw something();
}
mặc dù không có nhiều điểm khi làm theo cách này (có thể trong một số mã mẫu chung, điều này có thể hữu ích).
Quy tắc thống trị rất hữu ích, nhưng ít được biết đến. Nó nói rằng ngay cả khi trong một đường dẫn không phải là duy nhất qua mạng lớp cơ sở, việc tra cứu tên cho một thành viên bị ẩn một phần là duy nhất nếu thành viên đó thuộc về một lớp cơ sở ảo:
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(); } };
Tôi đã sử dụng điều này để triển khai hỗ trợ căn chỉnh tự động tìm ra sự liên kết chặt chẽ nhất bằng quy tắc thống trị.
Điều này không chỉ áp dụng cho các hàm ảo mà còn cho các tên typedef, các thành viên tĩnh / không ảo và bất cứ thứ gì khác. Tôi đã thấy nó được sử dụng để triển khai các đặc điểm ghi đè trong các chương trình meta.
struct C
vào ví dụ của mình ...? Chúc mừng.