thảo luận [C++] Thớt bàn luận, hỏi đáp thắc mắc về C++

try-catch bt là bt ntn? Đọc cho kỹ vào.

GCC enables it by default for languages like C++ that normally require exception handling, and disables it for languages like C that do not normally require it. However, you may need to enable this option when compiling C code that needs to interoperate properly with exception handlers written in C++.
By default vậy compile g++ có hay không có thì cũng như nhau nhỉ.
Với lại try catch không handle được segfault phải không thím ?
 
By default vậy compile g++ có hay không có thì cũng như nhau nhỉ.
Với lại try catch không handle được segfault phải không thím ?

Đây thuộc về 2 khái niệm không liên quan gì đến nhau:
  • Seg fault ==> Signal, khái niệm của HĐH,
  • Try catch ==> Exception, khái niệm của ngôn ngữ C++

Tuy nhiên dev vẫn có thể raise signal trong phần catch exception nếu muốn.
 
anh em nào cần kiếm chỗ để thi triển mấy tuyệt chiêu C++ trong topic thì inbox mình nhớ.
Cũng đang nghiên cứu C++ generic programming
 
By default vậy compile g++ có hay không có thì cũng như nhau nhỉ.
Với lại try catch không handle được segfault phải không thím ?
Giống như ông bribnt nói đó. segfault là signal. Muốn catch đc nó thì phải viết signal handler. Cái signal handler nó giới hạn nhiều thứ, k viết đc giống như function bt đâu, cẩn thận kẻo ăn hành.
https://stackoverflow.com/questions/1717991/throwing-an-exception-from-within-a-signal-handler

Còn muốn ăn sẵn thì search lib mà xài
https://github.com/Plaristote/segvcatch
:p
 
anh em nào cần kiếm chỗ để thi triển mấy tuyệt chiêu C++ trong topic thì inbox mình nhớ.
Cũng đang nghiên cứu C++ generic programming
mình đang mời ae vào làm cùng nhé, có gì ae giúp nhau. Chứ không phải nói trình mình cao nhé :(
 
Giống như ông bribnt nói đó. segfault là signal. Muốn catch đc nó thì phải viết signal handler. Cái signal handler nó giới hạn nhiều thứ, k viết đc giống như function bt đâu, cẩn thận kẻo ăn hành.
https://stackoverflow.com/questions/1717991/throwing-an-exception-from-within-a-signal-handler

Còn muốn ăn sẵn thì search lib mà xài
https://github.com/Plaristote/segvcatch
:p
Segfault nó crash mịa nó nguyên cái process luôn signal handler cái gì, bác hiểu câu hỏi không?
 
Cũng khác nhiều đó.
VD case này:

C++:
#include <cstdio>
struct A {
    int x;
    A() {}
};
struct B {
    int x;
    B() = default;
};

int main()
{
    A a;
    printf("A = %d\n", a.x);
    A a2{};
    printf("A2 = %d\n", a2.x);
    B b;
    printf("B = %d\n", b.x);
    B b2{};
    printf("B2 = %d\n", b2.x);
}

Thì chỉ có dòng cuối cùng mới đảm bảo ra bằng 0, còn lại là random. Vì chỉ có dòng đó mới thỏa mãn điều kiện để gọi zero-initialized.



https://en.cppreference.com/w/cpp/language/value_initialization
https://en.cppreference.com/w/cpp/language/default_initialization

Nếu build optimize thì hầu hết compiler sẽ tự zero-initialized, tuy nhiên cũng không nên quá dựa vào điều này.

// TB: test build local -O0 mới ra random, dùng mấy cái online nó có cơ chế gì đó nên ra 0 hết.
The effects of default initialization are: (1)

  • if T is a (possibly cv-qualified) non-POD (until C++11) class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object;
  • if T is an array type, every element of the array is default-initialized;
  • otherwise, no initialization is performed (see notes).
Theo ý hiểu của em thì do int x;// Thuộc struct A là Default Initialization, không thuộc một trong các trường hợp của (1)(otherwise, no initialization is performed) và nó không được khởi tạo bởi user-provided constructor nên giá trị của nó sẽ là indeterminate đúng không ạ?
 
Đây thuộc về 2 khái niệm không liên quan gì đến nhau:
  • Seg fault ==> Signal, khái niệm của HĐH,
  • Try catch ==> Exception, khái niệm của ngôn ngữ C++

Tuy nhiên dev vẫn có thể raise signal trong phần catch exception nếu muốn.
quan trọng là
Giống như ông bribnt nói đó. segfault là signal. Muốn catch đc nó thì phải viết signal handler. Cái signal handler nó giới hạn nhiều thứ, k viết đc giống như function bt đâu, cẩn thận kẻo ăn hành.
https://stackoverflow.com/questions/1717991/throwing-an-exception-from-within-a-signal-handler

Còn muốn ăn sẵn thì search lib mà xài
https://github.com/Plaristote/segvcatch
:p
Thanks bác đã share github đúng câu trả lời em cần,
 
Là sao bác, công ty bác đang tuyển người hả?
đúng rồi bác,
C++ nhiều cái hay nhưng lúc đi làm không phải lúc nào cũng có cơ hội apply. Ví dụ như mình đang phải limit ở C++ 14, vs cả phải chịu mấy cái rule convention như Misra.....
 
David Harvey là một Developer vẫn tiếp tục lập trình ở tuổi 60 và lời khuyên của ông là:

“Bạn có thể lập trình ở tuổi 30, thậm chí là 60. Các công ty lớn thường sử dụng một công nghệ trong khoảng thời gian dài. Tôi có rất nhiều việc để làm với ngôn ngữ C++, bởi vì không có nhiều người biết về nó rõ như tôi.”
Có thể nói không có ai trên thế giới này nắm rõ được hết các tính năng của c++.

via theNEXTvoz for iPhone
 
nhưng xài rvalue ref vẫn vướng 1 cách truyền
aVgiONl.png
đó là truyền const lvalue vào:
C++:
const std::string default{"default"};
foos.emplace_back(default, 0); // rvalue ref ko bind vào const lvalue được, báo lỗi
thế là phải viết thêm 1 ctor Foo(const std::string&, int) à? Cái này đụng chạm quy luật DRY dont repeat yourself
Dcnffay.png
C++ rác vậy xao
Dcnffay.png
ơ sau khi đọc lại value category của MS
OANgL56.png
thì toy mới thấy const std::string defaultlvalue, nhưng mà là non-modifiable lvalue. Mà lvalue thì ko thể biến thành rvalue được. Nhưng chỉ cần tạo 1 biến copy là xong:
C++:
const std::string default{"default"};
foos.emplace_back(std::string{default}, 0); // std::string{default} là prvalue (ko có identity, có moveable)
prvalue nó bind vào rvalue ref ngon lành
vậy xài rvalue ref thực ra ngon hơn pass by value ở trường hợp này
OANgL56.png


---

ban đầu C++ chỉ có 2 value category là lvalue và rvalue. Phân biệt 2 loại này bằng khái niệm identity. 1 biến có identity ở đây nghĩa là biến đó có địa chỉ và có thể sử dụng địa chỉ đó 1 cách an toàn:
If you have (or you can take) the memory address of a value, and use it safely, then the value has identity.
trong cái link MS kia có ví dụ rõ ràng hơn, vào đó xem ví dụ
OANgL56.png


sau này đẻ ra thêm khái niệm moveable
0KSdPUp.png
thì value category được chia làm 4 loại: i & m, !i & m, i & !m, !i & !m. Nhưng mà !i & !m vô giá trị nên chỉ còn 3 loại thoy:
  • i & !m: có identity nhưng ko moveable: là lvalue (theo toy nghĩ là giống như lvalue khái niệm cũ)
  • !i & m: ko có identity nhưng movable: là prvalue (theo toy nghĩ là giống như rvalue khái niệm cũ)
  • i & m: có identity và movable: là xvalue (khái niệm mới)
ngoài ra còn có gộp lại thành 2 category nữa là i và m: i bao gồm i & !m và i & m, gọi là glvalue. m bao gồm i & m và i & !m, gọi là rvalue.

có identity mà movable thì rất vô lý vì move xong identity cũ đâu còn sử dụng được nữa, vậy theo khái niệm cũ đâu phải là lvalue
Dcnffay.png
mà có identity thì cũng đâu phải rvalue khái niệm cũ
Dcnffay.png
đặt tên xvalue như là expiring thì cứ tưởng ví dụ như s.substr(1, 2) là xvalue vậy vì nó sống ko thọ, nhưng thực ra ở đây nó là prvalue
Dcnffay.png
vì nó ko có địa chỉ sử dụng được nhưng move được, nghĩa là !i & m nghĩa là prvalue. Rồi tự dưng đẻ ra glvalue và rvalue làm gì, đặt tên cũng như l`: rvalue (m) ở trên thì trùng với tên rvalue khái niệm cũ, còn lvalue (i & !m) thì lại trùng khái niệm mới. Sao đéo đổi tên thành i/m luôn cho rồi
aVgiONl.png


C++:
#include <utility>
void f(int n) {}
void f(int& n) {}
void f(const int& n) {}
void f(int&& n) {}
int main() {
    int n1 = 11;
    const int n2 = 22;
    f(n1);            // lvalue
    f(n2);            // const lvalue
    f(std::move(n1)); // xvalue
    f(int{n2});       // prvalue
}
thì nó in ra lỗi:
1676468098470.png

1676468128558.png

1676468248663.png
1676468212283.png


vậy:
lvalue có thể pass vào value, ref, cref: lvalue ko thể truyền vào rref được)
const lvalue có thể pass vào value, cref: const lvalue như lvalue ko thể truyền vào rref được, và có thêm ko truyền vào ref được)
xvalue có thể pass vào value, cref, rref: trừ ref ra
prvalue có thể pass vào value, cref, rref: trừ ref ra

lần này có thêm marker:
C++:
#include <iostream>
struct Category {
    Category& whoami() & { std::cout << "ref\n"; return *this; }
    const Category& whoami() const& { std::cout << "cref\n"; return *this; }
    Category&& whoami() && { std::cout << "rref\n"; return std::move(*this); }
    Category() = default;
    Category(const Category&) { std::cout << "copy ctor\n"; }
    Category(Category&&) { std::cout << "move ctor\n"; }
};
void f1(Category cat) {
    std::cout << "pass by value\n";
    cat.whoami();
}
void f2(Category& cat) {
    std::cout << "pass by ref\n";
    cat.whoami();
}
void f3(const Category& cat) {
    std::cout << "pass by cref\n";
    cat.whoami();
}
void f4(Category&& cat) {
    std::cout << "pass by rref\n";
    cat.whoami();
}
int main() {
    Category cat1;
    const Category cat2;
    f1(cat1.whoami()); // lvalue
    f2(cat1.whoami()); // lvalue
    f3(cat1.whoami()); // lvalue
    std::cout << "---\n";
    f1(cat2.whoami()); // const lvalue
    f3(cat2.whoami()); // const lvalue
    std::cout << "---\n";
    f1(std::move(cat1).whoami()); // xvalue
    f3(std::move(cat1).whoami()); // xvalue
    f4(std::move(cat1).whoami()); // xvalue
    std::cout << "---\n";
    f1(Category{});     // prvalue
    f3(Category{});     // prvalue
    f4(Category{});     // prvalue
    std::cout << "---\n";
    f1(Category{cat2}); // prvalue
    f3(Category{cat2}); // prvalue
    f4(Category{cat2}); // prvalue
}
output:
Code:
ref
copy ctor
pass by value
ref
ref
pass by ref
ref
ref
pass by cref
cref
---
cref
copy ctor
pass by value
ref
cref
pass by cref
cref
---
rref
move ctor
pass by value
ref
rref
pass by cref
cref
rref
pass by rref
ref
---
pass by value
ref
pass by cref
cref
pass by rref
ref
---
copy ctor
pass by value
ref
copy ctor
pass by cref
cref
copy ctor
pass by rref
ref
thì thấy rref sau khi truyền vào value hoặc rref thì trong thân hàm nó bị biến thành ref nghĩa là nó biến thành lvalue, vì nó được đặt tên là cat ở trỏng. Named rvalue ref = lvalue.
lịt pẹ ko biết tìm official doc phải đi thí nghiệm như du túp bơ rác
Wf29Rhg.png
 
Last edited:
ơ sau khi đọc lại value category của MS
OANgL56.png
thì toy mới thấy const std::string defaultlvalue, nhưng mà là non-modifiable lvalue. Mà lvalue thì ko thể biến thành rvalue được. Nhưng chỉ cần tạo 1 biến copy là xong:
C++:
const std::string default{"default"};
foos.emplace_back(std::string{default}, 0); // std::string{default} là prvalue (ko có identity, có moveable)
prvalue nó bind vào rvalue ref ngon lành
vậy xài rvalue ref thực ra ngon hơn pass by value ở trường hợp này
OANgL56.png


---

ban đầu C++ chỉ có 2 value category là lvalue và rvalue. Phân biệt 2 loại này bằng khái niệm identity. 1 biến có identity ở đây nghĩa là biến đó có địa chỉ và có thể sử dụng địa chỉ đó 1 cách an toàn:

trong cái link MS kia có ví dụ rõ ràng hơn, vào đó xem ví dụ
OANgL56.png


sau này đẻ ra thêm khái niệm moveable
0KSdPUp.png
thì value category được chia làm 4 loại: i & m, !i & m, i & !m, !i & !m. Nhưng mà !i & !m vô giá trị nên chỉ còn 3 loại thoy:
  • i & !m: có identity nhưng ko moveable: là lvalue (theo toy nghĩ là giống như lvalue khái niệm cũ)
  • !i & m: ko có identity nhưng movable: là prvalue (theo toy nghĩ là giống như rvalue khái niệm cũ)
  • i & m: có identity và movable: là xvalue (khái niệm mới)
ngoài ra còn có gộp lại thành 2 category nữa là i và m: i bao gồm i & !m và i & m, gọi là glvalue. m bao gồm i & m và i & !m, gọi là rvalue.

có identity mà movable thì rất vô lý vì move xong identity cũ đâu còn sử dụng được nữa, vậy theo khái niệm cũ đâu phải là lvalue
Dcnffay.png
mà có identity thì cũng đâu phải rvalue khái niệm cũ
Dcnffay.png
đặt tên xvalue như là expiring thì cứ tưởng ví dụ như s.substr(1, 2) là xvalue vậy vì nó sống ko thọ, nhưng thực ra ở đây nó là prvalue
Dcnffay.png
vì nó ko có địa chỉ sử dụng được nhưng move được, nghĩa là !i & m nghĩa là prvalue. Rồi tự dưng đẻ ra glvalue và rvalue làm gì, đặt tên cũng như l`: rvalue (m) ở trên thì trùng với tên rvalue khái niệm cũ, còn lvalue (i & !m) thì lại trùng khái niệm mới. Sao đéo đổi tên thành i/m luôn cho rồi
aVgiONl.png


C++:
#include <utility>
void f(int n) {}
void f(int& n) {}
void f(const int& n) {}
void f(int&& n) {}
int main() {
    int n1 = 11;
    const int n2 = 22;
    f(n1);            // lvalue
    f(n2);            // const lvalue
    f(std::move(n1)); // xvalue
    f(int{n2});       // prvalue
}
thì nó in ra lỗi:
View attachment 1664468
View attachment 1664469
View attachment 1664482View attachment 1664481

vậy:
lvalue có thể pass vào value, ref, cref: lvalue ko thể truyền vào rref được)
const lvalue có thể pass vào value, cref: const lvalue như lvalue ko thể truyền vào rref được, và có thêm ko truyền vào ref được)
xvalue có thể pass vào value, cref, rref: trừ ref ra
prvalue có thể pass vào value, cref, rref: trừ ref ra

lần này có thêm marker:
C++:
#include <iostream>
struct Category {
    Category& whoami() & { std::cout << "ref\n"; return *this; }
    const Category& whoami() const& { std::cout << "cref\n"; return *this; }
    Category&& whoami() && { std::cout << "rref\n"; return std::move(*this); }
    Category() = default;
    Category(const Category&) { std::cout << "copy ctor\n"; }
    Category(Category&&) { std::cout << "move ctor\n"; }
};
void f1(Category cat) {
    std::cout << "pass by value\n";
    cat.whoami();
}
void f2(Category& cat) {
    std::cout << "pass by ref\n";
    cat.whoami();
}
void f3(const Category& cat) {
    std::cout << "pass by cref\n";
    cat.whoami();
}
void f4(Category&& cat) {
    std::cout << "pass by rref\n";
    cat.whoami();
}
int main() {
    Category cat1;
    const Category cat2;
    f1(cat1.whoami()); // lvalue
    f2(cat1.whoami()); // lvalue
    f3(cat1.whoami()); // lvalue
    std::cout << "---\n";
    f1(cat2.whoami()); // const lvalue
    f3(cat2.whoami()); // const lvalue
    std::cout << "---\n";
    f1(std::move(cat1).whoami()); // xvalue
    f3(std::move(cat1).whoami()); // xvalue
    f4(std::move(cat1).whoami()); // xvalue
    std::cout << "---\n";
    f1(Category{});     // prvalue
    f3(Category{});     // prvalue
    f4(Category{});     // prvalue
    std::cout << "---\n";
    f1(Category{cat2}); // prvalue
    f3(Category{cat2}); // prvalue
    f4(Category{cat2}); // prvalue
}
output:
Code:
ref
copy ctor
pass by value
ref
ref
pass by ref
ref
ref
pass by cref
cref
---
cref
copy ctor
pass by value
ref
cref
pass by cref
cref
---
rref
move ctor
pass by value
ref
rref
pass by cref
cref
rref
pass by rref
ref
---
pass by value
ref
pass by cref
cref
pass by rref
ref
---
copy ctor
pass by value
ref
copy ctor
pass by cref
cref
copy ctor
pass by rref
ref
thì thấy rref sau khi truyền vào value hoặc rref thì trong thân hàm nó bị biến thành ref nghĩa là nó biến thành lvalue, vì nó được đặt tên là cat ở trỏng. Named rvalue ref = lvalue.
lịt pẹ ko biết tìm official doc phải đi thí nghiệm như du túp bơ rác
Wf29Rhg.png
cref chắc là constant ref, rref là gì đấy bác :(
 
target_include_directories(target_name {PUBLIC|PRIVATE|INTERFACE} directories...)

Thím nào giải thích giúp mình cái scope kia của target_include_directories trong cmake với. Kiến thức cmake mình bằng 0 nên trong lúc học không hiểu nó.
 
target_include_directories(target_name {PUBLIC|PRIVATE|INTERFACE} directories...)

Thím nào giải thích giúp mình cái scope kia của target_include_directories trong cmake với. Kiến thức cmake mình bằng 0 nên trong lúc học không hiểu nó.
ví dụ có 3 lib/exe ở đây: A, B, C. C xài B, B có thể xài hoặc ko xài A.
có 4 trường hợp ở đây:
  • B ko xài A, C cũng ko xài A: trường hợp này thì khỏi cần include/link A vào B
  • B ko xài A nhưng C cần xài A: trường hợp này là B INTERFACE A, được dùng khi B là header only lib: B có file b.h nhưng B ko cần xài, chỉ có những target xài B mới cần include b.h, vậy B INTERFACE b.h
  • B xài A nhưng C ko xài A: trường hợp này là B PRIVATE A: ví dụ A là C library, B là C++ library wrap các hàm của A lại theo C++, B link A statically, các target khác xài B ko cần biết tới A nữa, thì B PRIVATE A. Hoặc C là executable thì làm gì có thằng lào xài C được nữa, cứ C PRIVATE B hết.
  • B xài A và C cũng xài A: trường hợp này là B PUBLIC A: ví dụ lib B xài lib A và extends nó hay wrap nó lại cho tiện xài, nhưng vẫn cho phép các target khác xài B sử dụng full các tính năng từ A thì B PUBLIC A, hoặc để khi C PRIVATE B mà B link dynamically tới A thì phải viết C PRIVATE A B để cmake nó tự động copy A.dll files nếu B PRIVATE A, còn nếu B PUBLIC A thì chỉ cần C PRIVATE B là cmake copy A.dll và B.dll tới C.exe rồi.
 
Last edited:
Back
Top