Những khả năng mới nào mà người dùng định nghĩa bằng chữ thêm vào C ++?


139

C ++ 11 giới thiệu literals người dùng định nghĩa mà sẽ cho phép sự ra đời của cú pháp đen mới dựa trên literals hiện có ( int, hex, string, float) để cho bất kỳ loại sẽ có thể có một bài thuyết trình theo nghĩa đen.

Ví dụ:

// imaginary numbers
std::complex<long double> operator "" _i(long double d) // cooked form
{ 
    return std::complex<long double>(0, d); 
}
auto val = 3.14_i; // val = complex<long double>(0, 3.14)

// binary values
int operator "" _B(const char*); // raw form
int answer = 101010_B; // answer = 42

// std::string
std::string operator "" _s(const char* str, size_t /*length*/) 
{ 
    return std::string(str); 
}

auto hi = "hello"_s + " world"; // + works, "hello"_s is a string not a pointer

// units
assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

Thoạt nhìn điều này có vẻ rất tuyệt nhưng tôi tự hỏi nó thực sự có thể áp dụng như thế nào, khi tôi cố gắng nghĩ đến việc có các hậu tố _AD_BCtạo ngày tôi thấy rằng nó có vấn đề do lệnh của nhà điều hành. 1974/01/06_ADđầu tiên sẽ đánh giá 1974/01(dưới dạng đơn giản int) và chỉ sau đó 06_AD(không nói gì về tháng 8 và tháng 9 phải được viết mà không có 0lý do bát phân). Điều này có thể được giải quyết bằng cách sử dụng cú pháp 1974-1/6_ADđể thứ tự đánh giá toán tử hoạt động nhưng thật khó hiểu.

Vì vậy, câu hỏi của tôi sôi nổi là đây, bạn có cảm thấy tính năng này sẽ tự biện minh? Những từ khác mà bạn muốn xác định sẽ làm cho mã C ++ của bạn dễ đọc hơn?


Cú pháp cập nhật để phù hợp với bản nháp cuối cùng vào tháng 6 năm 2011


8
Tôi sẽ bỏ phiếu để đóng cái này. Tiêu đề là khá rõ ràng gây viêm.
Cún con

76
@DeadMG, nếu bạn gặp vấn đề với tiêu đề, bạn có thể chỉnh sửa nó. Thật là một chút buồn cười khi cố gắng đóng một câu hỏi 3 tuổi có 11 câu hỏi và 8 mục yêu thích. (Không phải đề cập rằng nghi thức trên trang web này đã thay đổi trong 3 năm qua).
Motti

5
Tôi nghĩ rằng bạn có một lỗi trong ví dụ của bạn: string operator "" _s(const char*s);"không thể được sử dụng để phân tích cú pháp "hello"_s". Đây là một chuỗi ký tự và sẽ tìm kiếm toán tử với một size_ttham số bổ sung . Tôi có đúng không
Towi

1
Một điều tôi băn khoăn là liệu có nên viết "C di động" trong C ++ hay không, thay thế các kiểu như uint16_thành vi của nó phụ thuộc vào thực thi, với các kiểu tương tự uwrap16unum16hành vi của chúng sẽ độc lập với thực thi, như vậy có uwrap16 w=1; unum16 n=1;biểu thức w-2n-2sẽ mang lại (uwrap16)65535(int)-1, tương ứng [ uint16_tsẽ mang lại kết quả đầu tiên trên các hệ thống có int16 bit và lần thứ hai trên các hệ thống intlớn hơn]. Vấn đề lớn nhất tôi thấy là xử lý chữ số.
1/5/2015

1
Việc có thể có các chữ số hoạt động trơn tru với các kiểu số hành vi được xác định khác có vẻ như nó sẽ cho phép các loại đó được sử dụng để tạo một ngôn ngữ mà mã muốn thực hiện các hành động phụ thuộc vào thực thi có thể làm như vậy mà không cần phải dựa vào việc triển khai- hành vi xác định. Có một vài nơi mà IDB vẫn sẽ không thể tránh khỏi bởi vì những thứ như khác biệt con trỏ và sizeoftrả về các kiểu số nguyên phụ thuộc vào triển khai, nhưng tình huống vẫn có thể được cải thiện hơn nhiều so với thực tế. Bạn sẽ nghĩ gì về khái niệm đó?
1/5/2015

Câu trả lời:


71

Đây là trường hợp có lợi thế khi sử dụng các chữ do người dùng định nghĩa thay vì lệnh gọi hàm tạo:

#include <bitset>
#include <iostream>

template<char... Bits>
  struct checkbits
  {
    static const bool valid = false;
  };

template<char High, char... Bits>
  struct checkbits<High, Bits...>
  {
    static const bool valid = (High == '0' || High == '1')
                   && checkbits<Bits...>::valid;
  };

template<char High>
  struct checkbits<High>
  {
    static const bool valid = (High == '0' || High == '1');
  };

template<char... Bits>
  inline constexpr std::bitset<sizeof...(Bits)>
  operator"" _bits() noexcept
  {
    static_assert(checkbits<Bits...>::valid, "invalid digit in binary string");
    return std::bitset<sizeof...(Bits)>((char []){Bits..., '\0'});
  }

int
main()
{
  auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits;
  std::cout << bits << std::endl;
  std::cout << "size = " << bits.size() << std::endl;
  std::cout << "count = " << bits.count() << std::endl;
  std::cout << "value = " << bits.to_ullong() << std::endl;

  //  This triggers the static_assert at compile time.
  auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits;

  //  This throws at run time.
  std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits");
}

Ưu điểm là ngoại lệ thời gian chạy được chuyển thành lỗi thời gian biên dịch. Bạn không thể thêm xác nhận tĩnh vào ctor bitet lấy một chuỗi (ít nhất là không có đối số mẫu chuỗi).


7
Bạn có thể làm điều tương tự bằng cách cung cấp cho std :: bitset một hàm tạo constexpr thích hợp.
Nicol Bolas

1
@NicolBolas Bạn nói đúng. Tôi thực sự ngạc nhiên khi không có ai ở đó. Có lẽ chúng ta nên đề xuất một hoặc hai cho năm 2014 nếu chưa quá muộn.
emsr

192

Ngay từ cái nhìn đầu tiên, nó có vẻ là đường cú pháp đơn giản.

Nhưng khi nhìn sâu hơn, chúng ta thấy nó còn hơn cả cú pháp, vì nó mở rộng các tùy chọn của người dùng C ++ để tạo ra các loại do người dùng định nghĩa hoạt động chính xác như các loại tích hợp riêng biệt. Trong đó, "phần thưởng" nhỏ này là một bổ sung C ++ 11 rất thú vị cho C ++.

Chúng ta có thực sự cần nó trong C ++ không?

Tôi thấy một vài cách sử dụng mã tôi đã viết trong những năm qua, nhưng chỉ vì tôi không sử dụng nó trong C ++ không có nghĩa là nó không thú vị đối với một nhà phát triển C ++ khác .

Chúng tôi đã sử dụng trong C ++ (và trong C, tôi đoán), nghĩa đen do trình biên dịch xác định, để nhập số nguyên là số nguyên ngắn hoặc dài, số thực là số float hoặc double (hoặc thậm chí dài gấp đôi) và chuỗi ký tự là ký tự bình thường hoặc rộng .

Trong C ++, chúng tôi có khả năng tạo các kiểu của riêng mình (ví dụ: các lớp), có khả năng không có chi phí hoạt động (nội tuyến, v.v.). Chúng tôi có khả năng thêm các toán tử vào các kiểu của chúng, để chúng hoạt động giống như các kiểu dựng sẵn tương tự, cho phép các nhà phát triển C ++ sử dụng ma trận và các số phức một cách tự nhiên nếu chúng được thêm vào ngôn ngữ. Chúng tôi thậm chí có thể thêm các toán tử cast (thường là một ý tưởng tồi, nhưng đôi khi, đó chỉ là giải pháp phù hợp).

Chúng tôi vẫn bỏ lỡ một điều để các loại người dùng hoạt động như các loại tích hợp: nghĩa đen do người dùng định nghĩa.

Vì vậy, tôi đoán đó là một sự phát triển tự nhiên của ngôn ngữ, nhưng phải hoàn chỉnh nhất có thể: " Nếu bạn muốn tạo một loại và bạn muốn nó hoạt động nhiều nhất có thể như các loại tích hợp, đây là các công cụ. .. "

Tôi đoán nó rất giống với quyết định của .NET về việc biến mọi cấu trúc nguyên thủy, bao gồm cả booleans, số nguyên, v.v. và có tất cả các cấu trúc xuất phát từ Object. Quyết định này một mình đưa .NET vượt xa tầm với của Java khi làm việc với các nguyên thủy, bất kể việc hack / unboxing bao nhiêu mà Java sẽ thêm vào đặc tả của nó.

Bạn có thực sự cần nó trong C ++?

Câu hỏi này dành cho BẠN trả lời. Không phải Bjarne Stroustrup. Không phải thảo mộc Sutter. Không phải bất cứ thành viên nào của ủy ban tiêu chuẩn C ++. Đây là lý do tại sao bạn có sự lựa chọn trong C ++ và họ sẽ không giới hạn ký hiệu hữu ích cho các loại tích hợp.

Nếu bạn cần nó, thì đó là một bổ sung được chào đón. Nếu bạn không, tốt ... Đừng sử dụng nó. Nó sẽ không mất gì.

Chào mừng bạn đến với C ++, ngôn ngữ mà các tính năng là tùy chọn.

Bồng bềnh ??? Cho tôi xem phức tạp của bạn !!!

Có một sự khác biệt giữa cồng kềnh và phức tạp (ý định chơi chữ).

Giống như được hiển thị bởi Niels tại Những khả năng mới nào mà người dùng do người dùng định nghĩa thêm vào C ++? , việc có thể viết một số phức là một trong hai tính năng được thêm "gần đây" vào C và C ++:

// C89:
MyComplex z1 = { 1, 2 } ;

// C99: You'll note I is a macro, which can lead
// to very interesting situations...
double complex z1 = 1 + 2*I;

// C++:
std::complex<double> z1(1, 2) ;

// C++11: You'll note that "i" won't ever bother
// you elsewhere
std::complex<double> z1 = 1 + 2_i ;

Bây giờ, cả loại "kép phức tạp" C99 và loại "std :: phức tạp" C ++ đều có thể được nhân, thêm, trừ, v.v., bằng cách sử dụng quá tải toán tử.

Nhưng trong C99, họ chỉ thêm một loại khác là loại tích hợp và hỗ trợ nạp chồng toán tử tích hợp. Và họ đã thêm một tính năng nghĩa đen tích hợp.

Trong C ++, họ chỉ sử dụng các tính năng hiện có của ngôn ngữ, thấy rằng tính năng nghĩa đen là sự phát triển tự nhiên của ngôn ngữ, và do đó đã thêm nó.

Trong C, nếu bạn cần tăng cường ký hiệu tương tự cho loại khác, bạn sẽ không gặp may cho đến khi vận động hành lang để thêm các hàm sóng lượng tử (hoặc điểm 3D hoặc bất kỳ loại cơ bản nào bạn đang sử dụng trong lĩnh vực công việc của mình) vào Tiêu chuẩn C như một loại tích hợp thành công.

Trong C ++ 11, bạn chỉ có thể tự làm điều đó:

Point p = 25_x + 13_y + 3_z ; // 3D point

Có phải là cồng kềnh? Không , nhu cầu là có, như thể hiện bằng cách cả hai phức C và C ++ cần một cách để biểu diễn các giá trị phức tạp theo nghĩa đen của chúng.

Có phải nó được thiết kế sai? Không , nó được thiết kế như mọi tính năng C ++ khác, với khả năng mở rộng.

Có phải chỉ cho mục đích ký hiệu? Không , vì nó thậm chí có thể thêm loại an toàn cho mã của bạn.

Ví dụ: hãy tưởng tượng một mã định hướng CSS:

css::Font::Size p0 = 12_pt ;       // Ok
css::Font::Size p1 = 50_percent ;  // Ok
css::Font::Size p2 = 15_px ;       // Ok
css::Font::Size p3 = 10_em ;       // Ok
css::Font::Size p4 = 15 ;         // ERROR : Won't compile !

Sau đó, rất dễ dàng để thực thi một kiểu gõ mạnh vào việc gán các giá trị.

Có nguy hiểm không?

Câu hỏi hay. Các chức năng này có thể được đặt tên? Nếu có, thì Jackpot!

Dù sao, giống như mọi thứ, bạn có thể tự sát nếu một công cụ được sử dụng không đúng cách . C rất mạnh và bạn có thể bắn đầu nếu bạn lạm dụng súng C. C ++ có súng C, nhưng cũng có dao mổ, taser và bất kỳ công cụ nào khác bạn sẽ tìm thấy trong bộ công cụ. Bạn có thể lạm dụng dao mổ và tự chảy máu đến chết. Hoặc bạn có thể xây dựng mã rất thanh lịch và mạnh mẽ.

Vì vậy, giống như mọi tính năng của C ++, bạn có thực sự cần nó không? Đó là câu hỏi bạn phải trả lời trước khi sử dụng nó trong C ++. Nếu bạn không, nó sẽ không mất gì. Nhưng nếu bạn thực sự cần nó, ít nhất, ngôn ngữ sẽ không làm bạn thất vọng.

Ví dụ ngày?

Theo tôi, lỗi của bạn là do bạn đang trộn các toán tử:

1974/01/06AD
    ^  ^  ^

Điều này không thể tránh được, bởi vì / là một toán tử, trình biên dịch phải diễn giải nó. Và, AFAIK, đó là một điều tốt.

Để tìm một giải pháp cho vấn đề của bạn, tôi sẽ viết nghĩa đen theo một cách khác. Ví dụ:

"1974-01-06"_AD ;   // ISO-like notation
"06/01/1974"_AD ;   // french-date-like notation
"jan 06 1974"_AD ;  // US-date-like notation
19740106_AD ;       // integer-date-like notation

Cá nhân, tôi sẽ chọn số nguyên và ngày ISO, nhưng nó phụ thuộc vào nhu cầu của BẠN. Đó là toàn bộ quan điểm cho phép người dùng xác định tên theo nghĩa đen của riêng mình.


1
Cảm ơn bạn, tôi và các tính cách thay thế khác của tôi đã được phát hiện. Nghiêm trọng hơn, tôi đã viết điều này một mình, nhưng có lẽ tôi đang sử dụng một số biểu hiện của ngôn ngữ mẹ đẻ của tôi và họ không dịch tốt sang tiếng Anh.
paercebal

Đối với "các phần khác nhau", như thể hiện trong tiêu đề của họ, xin lỗi, tôi đoán điều này tổ chức một bài viết khá dài. Đối với văn bản in đậm, đó là tóm tắt của đoạn họ đang ở. Mọi người chỉ muốn thông tin mà không cần biện minh có thể giới hạn việc đọc của họ trên tiêu đề và văn bản in đậm.
paercebal

3
+1. Giải thích thực sự tốt đẹp. Chúng tôi đang chờ đợi điều này được thực hiện. Nó thực sự quan trọng đối với chúng tôi. Chúng tôi làm việc trên MDE (Model-Driven Engineering) và thấy đây là một sự cần thiết. Tôi đang thêm một câu trả lời dưới đây để giải thích trường hợp của chúng tôi.
Diego Sevilla

9
@TGV :: you can write 1+2i, but you still can't write a+bi, so there's absolutely no pointNgay cả việc bỏ qua a+biví dụ của bạn là vô lý, thực tế bạn coi đó là "tần số thấp" không có nghĩa là tất cả mọi người làm. . . Nhìn vào bức tranh lớn, vấn đề là đảm bảo các đối tượng do người dùng xác định có thể được coi là công dân hạng nhất của ngôn ngữ, cũng như các kiểu dựng sẵn. Vì vậy, nếu bạn có thể viết 1.5f1000UL, tại sao bạn không thể viết 25ihoặc thậm chí 100101b? Trái với C và Java, các kiểu người dùng không được coi là công dân hạng hai của ngôn ngữ trong C ++.
paercebal

3
@Anton :: Most of data still comes from IOCó rất nhiều giá trị được mã hóa cứng trong mã. Nhìn vào tất cả các booleans, tất cả các số nguyên, tất cả các nhân đôi trong mã, bởi vì nó thuận tiện hơn để viết x = 2 * y ;thay vì x = Two * ynơi Twolà một hằng số được gõ mạnh . Người dùng định nghĩa bằng chữ cho phép chúng ta đặt một loại trên đó và viết: x = 2_speed * y ;và yêu cầu trình biên dịch xác minh rằng phép tính có ý nghĩa. . . Tất cả đều liên quan đến việc đánh máy mạnh mẽ. . . Có lẽ bạn sẽ không sử dụng nó. Nhưng tôi chắc chắn sẽ, ngay khi tôi có thể sử dụng trình biên dịch hỗ trợ C ++ 11 tại nơi làm việc.
paercebal

36

Nó rất hay cho mã toán học. Trong tâm trí tôi có thể thấy việc sử dụng cho các toán tử sau:

độ cho độ. Điều đó làm cho việc viết các góc tuyệt đối trực quan hơn nhiều.

double operator ""_deg(long double d)
{ 
    // returns radians
    return d*M_PI/180; 
}

Nó cũng có thể được sử dụng cho các biểu diễn điểm cố định khác nhau (vẫn được sử dụng trong lĩnh vực DSP và đồ họa).

int operator ""_fix(long double d)
{ 
    // returns d as a 1.15.16 fixed point number
    return (int)(d*65536.0f); 
}

Chúng trông giống như những ví dụ đẹp làm thế nào để sử dụng nó. Chúng giúp làm cho các hằng số trong mã dễ đọc hơn. Đó cũng là một công cụ khác để làm cho mã không thể đọc được, nhưng chúng ta đã có quá nhiều công cụ lạm dụng mà một công cụ khác không gây hại nhiều.


1
"Nhưng chúng tôi đã lạm dụng nhiều công cụ đến mức một lần nữa không gây hại nhiều. " Wow, tôi hy vọng đó không phải là triết lý đằng sau tất cả các tính năng c ++ [x] 1234567890 tràn ngập gần đây. Hãy tưởng tượng bạn phải học một thư viện sử dụng tất cả các định dạng đó, cộng với mười định dạng tệp để cấu hình và hai công cụ xử lý trước và sau mã của bạn ...
masterxilo

@masterxilo Tất nhiên, trong thực tế, ví dụ của bạn là vô lý: UDL không khó học hơn các hàm, vì chỉ cần cú pháp đường cho chúng - & bên cạnh đó, bất kỳ lib tốt nào cũng chỉ sử dụng các tính năng theo yêu cầu để cải thiện UX - & tài liệu chính xác là gì tất cả các phương tiện. Nếu ai đó lạm dụng một tính năng để tạo mã không thể đọc được (giả sử rằng điều đó hoàn toàn có thể tránh được trong công việc của họ ...), thì đó không phải là do tính năng đó bị lỗi, chỉ là việc sử dụng. Thêm vào đó, một người không thể đọc được là bánh mì và bơ của người khác. Đó là tất cả các ý kiến ​​- & tùy chọn . Nếu bạn không thích chúng, đừng lo lắng! Bạn không cần phải sử dụng chúng. Những người khác có thể .
gạch dưới

17

Các UDL được đặt tên (và có thể được nhập bằng cách sử dụng khai báo / chỉ thị, nhưng bạn không thể đặt tên không gian một cách rõ ràng theo nghĩa đen 3.14std::i), điều đó có nghĩa là (hy vọng) sẽ không có nhiều xung đột.

Thực tế là chúng thực sự có thể được tạo khuôn mẫu (và constexpr'd) có nghĩa là bạn có thể thực hiện một số nội dung khá mạnh mẽ với UDL. Các tác giả của Bigint sẽ thực sự hạnh phúc, vì cuối cùng họ có thể có các hằng số lớn tùy ý, được tính toán tại thời gian biên dịch (thông qua constexpr hoặc các mẫu).

Tôi chỉ buồn là chúng ta sẽ không thấy một vài chữ có ích trong tiêu chuẩn (từ vẻ ngoài của nó), như scho std::stringicho đơn vị tưởng tượng.

Lượng thời gian mã hóa sẽ được lưu bởi UDL thực sự không cao, nhưng khả năng đọc sẽ được tăng lên rất nhiều và ngày càng có nhiều phép tính có thể được chuyển sang thời gian biên dịch để thực hiện nhanh hơn.


Cảm ơn vì đã làm sáng tỏ quan điểm về không gian tên ... Tôi đã tự hỏi điều đó.
Nathan Reed

12

Hãy để tôi thêm một chút bối cảnh. Đối với công việc của chúng tôi, người dùng xác định nghĩa đen là rất cần thiết. Chúng tôi làm việc trên MDE (Model-Driven Engineering). Chúng tôi muốn xác định mô hình và siêu dữ liệu trong C ++. Chúng tôi thực sự đã triển khai ánh xạ từ Ecore sang C ++ ( EMF4CPP ).

Vấn đề xảy ra khi có thể định nghĩa các phần tử mô hình là các lớp trong C ++. Chúng tôi đang thực hiện phương pháp chuyển đổi siêu dữ liệu (Ecore) thành các mẫu với các đối số. Các đối số của mẫu là các đặc điểm cấu trúc của các loại và các lớp. Ví dụ, một lớp có hai thuộc tính int sẽ giống như:

typedef ::ecore::Class< Attribute<int>, Attribute<int> > MyClass;

Hoever, hóa ra mọi yếu tố trong một mô hình hoặc siêu mô hình, thường có một tên. Chúng tôi xin viết:

typedef ::ecore::Class< "MyClass", Attribute< "x", int>, Attribute<"y", int> > MyClass;

NHƯNG, C ++, cũng như C ++ 0x không cho phép điều này, vì các chuỗi bị cấm làm đối số cho các mẫu. Bạn có thể viết tên char bằng char, nhưng điều này được thừa nhận là một mớ hỗn độn. Với nghĩa đen do người dùng định nghĩa, chúng ta có thể viết một cái gì đó tương tự. Giả sử chúng tôi sử dụng "_n" để xác định tên thành phần mô hình (Tôi không sử dụng cú pháp chính xác, chỉ để tạo ý tưởng):

typedef ::ecore::Class< MyClass_n, Attribute< x_n, int>, Attribute<y_n, int> > MyClass;

Cuối cùng, việc có các định nghĩa này làm mẫu giúp chúng ta thiết kế các thuật toán để vượt qua các phần tử mô hình, biến đổi mô hình, v.v ... thực sự hiệu quả, bởi vì thông tin kiểu, nhận dạng, biến đổi, v.v. được xác định bởi trình biên dịch tại thời điểm biên dịch.


4
Tôi thích rất rất nhiều các by the compiler at compile timephần ... :-)
paercebal

12

Bjarne Stroustrup nói về UDL trong bài nói chuyện C ++ 11 này , trong phần đầu tiên về giao diện giàu kiểu, khoảng 20 phút.

Đối số cơ bản của ông đối với UDL có dạng tam đoạn luận:

  1. Các loại "tầm thường", nghĩa là các kiểu nguyên thủy tích hợp, chỉ có thể bắt lỗi các loại tầm thường. Các giao diện với các loại phong phú hơn cho phép hệ thống loại bắt được nhiều loại lỗi hơn.

  2. Các loại lỗi loại mã được gõ nhiều có thể ảnh hưởng đến mã thực. (Ông đưa ra ví dụ về Tàu quỹ đạo Khí hậu Sao Hỏa, đã thất bại thảm hại do lỗi kích thước trong một hằng số quan trọng).

  3. Trong mã thực, các đơn vị hiếm khi được sử dụng. Mọi người không sử dụng chúng, bởi vì việc tính toán thời gian chạy hoặc chi phí bộ nhớ để tạo ra các kiểu phong phú là quá tốn kém và việc sử dụng mã đơn vị templated C ++ đã tồn tại rất xấu đến nỗi không ai sử dụng nó. (Theo kinh nghiệm, không ai sử dụng nó, mặc dù các thư viện đã tồn tại được một thập kỷ).

  4. Do đó, để khiến các kỹ sư sử dụng các đơn vị trong mã thực, chúng tôi cần một thiết bị (1) không phát sinh chi phí thời gian chạy và (2) có thể chấp nhận được về mặt lý thuyết.


9

Hỗ trợ kiểm tra kích thước thời gian biên dịch là biện minh duy nhất cần thiết.

auto force = 2_N; 
auto dx = 2_m; 
auto energy = force * dx; 

assert(energy == 4_J); 

Xem ví dụ PhysUnits-CT-Cpp11 , một thư viện nhỏ chỉ tiêu đề C ++ 11, C ++ 14 để phân tích thứ nguyên thời gian biên dịch và thao tác và chuyển đổi đơn vị / số lượng. Đơn giản hơn Boost.Units , không hỗ trợ các tự ký hiệu đơn vị như m, g, s, tiền tố số liệu như m, k, M, chỉ phụ thuộc vào thư viện C ++ tiêu chuẩn, chỉ SI, quyền hạn tích phân của kích thước.


Hoặc xem các đơn vị , thư viện thời gian biên dịch, chỉ tiêu đề, phân tích thứ nguyên và thư viện chuyển đổi đơn vị được xây dựng trên c ++ 14 mà không phụ thuộc bởi Nic Holthaus .
Martin Moene

6

Hmm ... Tôi chưa nghĩ về tính năng này. Mẫu của bạn đã được nghĩ ra và chắc chắn là thú vị. C ++ rất mạnh như bây giờ, nhưng thật không may, cú pháp được sử dụng trong các đoạn mã bạn đọc đôi khi quá phức tạp. Khả năng đọc là, nếu không phải tất cả, thì ít nhất là nhiều. Và một tính năng như vậy sẽ được hướng đến để dễ đọc hơn. Nếu tôi lấy ví dụ cuối cùng của bạn

assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

... Tôi tự hỏi làm thế nào bạn thể hiện điều đó ngày hôm nay. Bạn sẽ có một lớp KG và LB và bạn sẽ so sánh các đối tượng ẩn:

assert(KG(1.0f) == LB(2.2f));

Và điều đó cũng sẽ làm như vậy. Với các loại có tên hoặc loại dài hơn mà bạn không có hy vọng có được một hàm tạo đẹp như vậy cho các sans viết bộ điều hợp, nó có thể là một bổ sung tốt cho việc khởi tạo và khởi tạo đối tượng ngầm. Mặt khác, bạn cũng có thể tạo và khởi tạo các đối tượng bằng các phương thức.

Nhưng tôi đồng ý với Nils về toán học. Ví dụ, các hàm lượng giác C và C ++ yêu cầu đầu vào tính bằng radian. Tôi nghĩ theo độ, mặc dù vậy, một chuyển đổi ngầm rất ngắn như Nils đã đăng là rất hay.

Cuối cùng, nó sẽ là đường cú pháp, tuy nhiên, nó sẽ có ảnh hưởng nhẹ đến khả năng đọc. Và có lẽ sẽ dễ viết một số biểu thức hơn (sin (180.0deg) dễ viết hơn sin (deg (180.0)). Và sau đó sẽ có những người lạm dụng khái niệm này. Nhưng sau đó, những người lạm dụng ngôn ngữ nên sử dụng ngôn ngữ rất hạn chế hơn là một thứ gì đó biểu cảm như C ++.

Ah, bài viết của tôi về cơ bản không có gì ngoại trừ: nó sẽ ổn thôi, tác động sẽ không quá lớn. Chúng ta đừng lo lắng. :-)


5
Dấu ngoặc của bạn không cân bằng! Xin lỗi, OCD của tôi cũng ghét tôi.
X-Istence

3

Tôi chưa bao giờ cần hoặc muốn tính năng này (nhưng đây có thể là hiệu ứng Blub ). Phản ứng giật đầu gối của tôi là nó khập khiễng và có khả năng hấp dẫn những người nghĩ rằng điều đó thật tuyệt khi vận hành quá tải + cho bất kỳ hoạt động nào có thể được hiểu là thêm vào.


Tôi xác nhận: Bài viết rất thú vị.
paercebal

2

C ++ thường rất nghiêm ngặt về cú pháp được sử dụng - việc chặn bộ tiền xử lý không có nhiều thứ bạn có thể sử dụng để xác định cú pháp / ngữ pháp tùy chỉnh. Ví dụ: chúng ta có thể quá tải các operatos hiện có, nhưng chúng ta không thể định nghĩa những cái mới - IMO điều này rất phù hợp với tinh thần của C ++.

Tôi không bận tâm một số cách để mã nguồn tùy chỉnh hơn - nhưng điểm được chọn dường như rất cô lập với tôi, điều này làm tôi bối rối nhất.

Ngay cả mục đích sử dụng cũng có thể khiến việc đọc mã nguồn trở nên khó khăn hơn nhiều: một chữ cái có thể có tác dụng phụ rất lớn mà không có cách nào có thể xác định được từ ngữ cảnh. Với tính đối xứng với u, l và f, hầu hết các nhà phát triển sẽ chọn các chữ cái đơn.

Điều này cũng có thể biến phạm vi thành một vấn đề, sử dụng các chữ cái duy nhất trong không gian tên toàn cầu có thể sẽ bị coi là thực tiễn xấu và các công cụ được cho là trộn thư viện dễ dàng hơn (không gian tên và định danh mô tả) có thể sẽ đánh bại mục đích của nó.

Tôi thấy một số bằng khen kết hợp với "tự động", cũng kết hợp với một thư viện đơn vị như các đơn vị tăng , nhưng không đủ để xứng đáng với danh hiệu này.

Tôi tự hỏi, tuy nhiên, những ý tưởng thông minh mà chúng tôi đưa ra.


1
using single letters in global namespace will probably be considered bad practiceNhưng điều đó không liên quan: (A) UDL phải được xác định tại phạm vi không gian tên (không toàn cầu) ... có lẽ vì (B) chúng phải bao gồm một dấu gạch dưới sau đó> = 1 chữ cái, không chỉ chữ cái và các định danh như vậy trong NS toàn cầu được dành riêng để thực hiện. Đó là ít nhất 2 điểm so với ý tưởng rằng UDL vô tình tạo ra sự nhầm lẫn. Đối với việc phải phạm vi không gian tên làm giảm tiện ích của tính năng, đó là lý do tại sao stdlib tuyên bố chúng trong inline namespaces mà người dùng có thể nhập bán buôn nếu muốn.
gạch dưới

2

Tôi đã sử dụng nghĩa đen của người dùng cho các chuỗi nhị phân như thế này:

 "asd\0\0\0\1"_b

sử dụng hàm std::string(str, n)tạo để \0không cắt một nửa chuỗi. (Dự án thực hiện rất nhiều công việc với các định dạng tệp khác nhau.)

Điều này cũng hữu ích khi tôi bỏ std::stringqua ủng hộ một trình bao bọc cho std::vector.


-5

Dòng tiếng ồn trong đó là rất lớn. Cũng thật kinh khủng khi đọc.

Hãy cho tôi biết, họ có lý do bổ sung cú pháp mới với bất kỳ loại ví dụ nào không? Chẳng hạn, họ có vài chương trình đã sử dụng C ++ 0x không?

Đối với tôi, phần này:

auto val = 3.14_i

Không biện minh cho phần này:

std::complex<double> operator ""_i(long double d) // cooked form
{ 
    return std::complex(0, d);
}

Ngay cả khi bạn cũng sử dụng cú pháp i trong 1000 dòng khác. Nếu bạn viết, có lẽ bạn cũng viết 10000 dòng của một cái gì đó khác. Đặc biệt là khi bạn vẫn có thể sẽ viết hầu hết ở mọi nơi này:

std::complex<double> val = 3.14i

'auto' -keyword có thể được biện minh mặc dù, chỉ có lẽ. Nhưng hãy xem C ++, vì nó tốt hơn C ++ 0x ở khía cạnh này.

std::complex<double> val = std::complex(0, 3.14);

Nó giống như .. đơn giản. Thậm chí nghĩ rằng tất cả các dấu ngoặc nhọn và nhọn chỉ là khập khiễng nếu bạn sử dụng nó ở mọi nơi. Tôi không bắt đầu đoán cú pháp nào có trong C ++ 0x để biến std :: phức tạp thành phức tạp.

complex = std::complex<double>;

Đó có lẽ là một điều đơn giản, nhưng tôi không tin nó đơn giản trong C ++ 0x.

typedef std::complex<double> complex;

complex val = std::complex(0, 3.14);

Có lẽ? > :)

Dù sao, vấn đề là: viết 3.14i thay vì std :: phức (0, 3.14); không giúp bạn tiết kiệm nhiều thời gian nói chung trừ một vài trường hợp siêu đặc biệt.


10
@Cheery: Đối với bạn, "auto val = 3.14i" không biện minh cho mã được viết để hỗ trợ nó. Tôi có thể trả lời rằng, đối với tôi "printf ("% i ", 25)" không biện minh cho mã được viết cho printf. Bạn có thấy một mô hình?
paercebal

5
@Cheery: "Nhiễu đường trong điều đó là rất lớn". Không, đó không phải là ... "Cũng thật kinh khủng khi đọc". Đối số chủ quan của bạn rất thú vị, nhưng bạn nên xem xét quá tải toán tử nói chung để thấy mã cho tính năng này không có gì đáng ngạc nhiên / gây sốc ... Đối với một nhà phát triển C ++
paercebal

3
tự động sẽ giúp dễ đọc. xem xét sử dụng các interators trong một vòng lặp for: for (auto it = vec.begin (); it! = vec.end (); ++ it) ... Tôi biết về for_each, nhưng không thích phải tạo functor để sử dụng nó .
KitsuneYMG

1
@kts: Với C ++ 1x, chúng tôi sẽ có lambda và phạm vi cho
Joe D

3
Nếu dòng C ++ của bạn tốt hơn C ++ 0x, thì dòng của tôi vẫn tốt hơn. Viết chỉ : std::complex<double> val(0, 3.14);.
Ben Voigt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.