문제

C++11 소개하다 사용자 정의 리터럴 기존 리터럴을 기반으로 새로운 리터럴 구문을 도입할 수 있습니다(int, hex, string, float) 모든 유형이 문자 그대로 표시될 수 있도록 합니다.

예:

// 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

언뜻보기에 이것은 매우 멋져 보이지만 접미사를 갖는 것을 생각하려고 할 때 실제로 얼마나 적용 가능한지 궁금합니다. _AD 그리고 _BC 날짜 생성 운영자의 주문으로 인해 문제가 있는 것으로 확인되었습니다. 1974/01/06_AD 먼저 평가할 것이다 1974/01 (평범하게 ints) 그리고 나중에야 06_AD (8월과 9월은 설명 없이 써야 한다는 것은 말할 것도 없습니다. 0 8진수 이유).이 문제는 구문을 다음과 같이 하여 해결할 수 있습니다. 1974-1/6_AD 연산자 평가 순서는 작동하지만 투박합니다.

그래서 제 질문은 이렇습니다. 이 기능이 그 자체로 정당화될 것이라고 생각하시나요?C++ 코드를 더 읽기 쉽게 만들기 위해 정의하고 싶은 다른 리터럴은 무엇입니까?


2011년 6월 최종 초안에 맞게 구문이 업데이트되었습니다.

도움이 되었습니까?

해결책

다음은 생성자 호출 대신 사용자 정의 리터럴을 사용하는 데 이점이있는 경우입니다.

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

장점은 런타임 예외가 컴파일 타임 오류로 변환된다는 것입니다. static assert를 bitset ctor에 문자열을 가져갈 수 없었습니다 (적어도 문자열 템플릿 인수가 없음).

다른 팁

첫눈에, 그것은 단순한 구문 설탕 인 것 같습니다.

그러나 더 깊어 보일 때, 우리는 그것이 구문 설탕 이상인 것을 볼 수 있습니다. C ++ 사용자의 옵션을 확장하여 별개의 내장 유형과 똑같이 작동하는 사용자 정의 유형을 만듭니다. 이 작은 "보너스"는 C ++에 매우 흥미로운 C ++ 11 추가입니다.

C ++로 정말로 필요합니까?

지난 몇 년 동안 내가 쓴 코드에서 거의 용도가 거의 보이지 않지만 C ++에서 사용하지 않았다고해서 흥미롭지 않다는 의미는 아닙니다. 또 다른 C ++ 개발자.

우리는 C ++ (및 C, 추측), 컴파일러 정의 리터럴, 정수 번호를 짧거나 긴 정수로, 실수로 부동산 또는 이중 (또는 긴 이중), 일반 또는 넓은 숯으로 문자열을 입력했습니다. .

C ++에서는 우리 자신의 유형을 만들 가능성이있었습니다. (즉, 클래스), 잠재적으로 오버 헤드가 없음 (인라인 등). 우리는 운영자를 유형에 추가하고 유사한 내장 유형처럼 행동하도록 할 수 있었으며, 이는 C ++ 개발자가 언어 자체에 추가 된 경우와 같이 자연스럽게 매트릭스와 복소수를 사용할 수 있도록합니다. 캐스트 연산자를 추가 할 수도 있습니다 (일반적으로 나쁜 생각이지만 때로는 올바른 솔루션입니다).

우리는 여전히 사용자 유형이 내장 유형 인 사용자 정의 리터럴로 작동하도록 한 가지를 놓쳤다.

그래서 나는 그것이 언어에 대한 자연스러운 진화라고 생각하지만 가능한 한 완전해야합니다. "유형을 만들고 싶고 내장 유형만큼 가능한 많은 행동을 원한다면 여기 도구가 있습니다 ..."

나는 부울, 정수 등을 포함한 모든 원시를 구조물로 만들 겠다는 .NET의 결정과 매우 유사하다고 생각합니다. 이 결정만으로도 .NET은 Java가 사양에 추가 할 권투/Unboxing Hacks가 얼마나 많은지에 관계없이 프리미티브로 작업 할 때 Java의 범위를 훨씬 뛰어 넘습니다.

C ++로 정말로 필요합니까?

이 질문은입니다 대답합니다. Bjarne Stroustrup이 아닙니다. 허브 셔터가 아닙니다. C ++ 표준위원회의 구성원이 아닙니다. 이는 이유 C ++에서 선택할 수 있습니다, 그리고 유용한 표기법을 내장 유형만으로 제한하지 않습니다.

만약에 필요하면 환영받는 추가입니다. 만약에 잘하지 마십시오 ... 사용하지 마십시오. 비용이 들지 않습니다.

기능이 선택적 인 언어 인 C ++에 오신 것을 환영합니다.

부푼??? 당신의 단지를 보여주세요 !!!

팽창과 복잡한 사이에는 차이가 있습니다 (말장난 의도).

Niels에서 보여주는 것처럼 사용자 정의 리터럴이 C ++에 추가되는 새로운 기능은 무엇입니까?, 복소수를 쓸 수 있다는 것은 "최근"에 C와 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 ;

이제 C99 "Double Complex"유형 및 C ++ "STD :: Complex"유형은 연산자 과부하를 사용하여 곱하거나 추가, 빼기 등을 사용할 수 있습니다.

그러나 C99에서는 다른 유형을 내장 유형으로 추가했으며 내장 작업자가 과부하 지원을 추가했습니다. 그리고 그들은 또 다른 내장 리터럴 기능을 추가했습니다.

C ++에서는 방금 언어의 기존 기능을 사용하여 문자 그대로의 특징이 언어의 자연스러운 진화라는 것을 알았으며, 따라서 추가했습니다.

C에서는 다른 유형에 대해 동일한 표기법 향상이 필요한 경우 로비를 시작할 때까지 운이 좋지 않습니다. C 표준 내장 유형으로서의 표준이 성공합니다.

C ++ 11에서는 직접 할 수 있습니다.

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

부풀어 오르나요? 아니, C ++ 복합체 모두 문자 그대로 복합 값을 나타내는 방법이 필요한 방법에 의해 보여 주듯이 필요합니다.

잘못 디자인 되었습니까? 아니, 확장 성을 염두에두고 다른 모든 C ++ 기능으로 설계되었습니다.

표기법 목적으로 만 있습니까? 아니, 코드에 유형 안전을 추가 할 수도 있습니다.

예를 들어 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 !

그런 다음 값의 할당에 강력한 타이핑을 시행하는 것은 매우 쉽습니다.

위험한가요?

좋은 질문. 이러한 기능을 네임 스펙을 할 수 있습니까? 그렇다면 잭팟!

그래도, 모든 것과 마찬가지로 도구가 부적절하게 사용되면 스스로를 죽일 수 있습니다.. C는 강력하며 C 총을 오용하면 머리를 쏘 수 있습니다. C ++에는 C 건뿐만 아니라 메스, 테이저 및 툴킷에서 찾을 수있는 다른 도구도 있습니다. 메스를 오용하고 자신을 죽일 수 있습니다. 또는 매우 우아하고 강력한 코드를 구축 할 수 있습니다.

따라서 모든 C ++ 기능과 마찬가지로 정말로 필요합니까? C ++에서 사용하기 전에 대답 해야하는 질문입니다. 그렇지 않으면 비용이 들지 않습니다. 그러나 당신이 정말로 그것을 필요로한다면, 적어도 언어는 당신을 실망시키지 않을 것입니다.

날짜 예?

당신의 오류는 나에게 보이는 것 같습니다. 당신은 연산자를 혼합하고 있다는 것입니다.

1974/01/06AD
    ^  ^  ^

연산자이기 때문에 컴파일러가 해석해야하기 때문에 피할 수 없습니다. 그리고, 아바이, 그것은 좋은 일입니다.

당신의 문제에 대한 해결책을 찾으려면, 나는 다른 방법으로 문자를 쓸 것입니다. 예를 들어:

"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

개인적으로, 나는 정수와 ISO 날짜를 선택할 것이지만, 그것은 당신의 필요에 따라 다릅니다. 이것은 사용자가 자체 문자 이름을 정의하게하는 요점입니다.

수학 코드에 매우 좋습니다. 내 마음에서 나는 다음 운영자에 대한 사용을 볼 수 있습니다.

학위에 대한 deg. 그것은 절대 각도를 훨씬 더 직관적으로 만듭니다.

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

또한 다양한 고정 점 표현 (DSP 및 그래픽 분야에서 여전히 사용 중임)에도 사용할 수 있습니다.

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

이것들은 그것을 사용하는 방법처럼 보입니다. 코드의 상수를 더 읽기 쉽게 만드는 데 도움이됩니다. 코드도 읽을 수 없게 만드는 또 다른 도구이지만, 우리는 이미 너무 많은 도구를 남용하여 하나를 더 많이 아프지 않습니다.

UDL은 네임 스패치되어 있으며 (그리고 선언/지침을 사용하여 가져올 수 있지만, 문자 그대로의 이름을 명시 적으로 표시 할 수는 없습니다. 3.14std::i), 이는 (희망적으로) 많은 충돌이 없다는 것을 의미합니다.

그들이 실제로 템플릿 (그리고 constexpr'd) 될 수 있다는 사실은 UDL과 함께 꽤 강력한 일을 할 수 있다는 것을 의미합니다. Bigint 저자는 컴파일 타임 (ConstexPR 또는 템플릿을 통해)에 계산 된 임의로 큰 상수를 가질 수 있기 때문에 정말 행복 할 것입니다.

나는 우리가 표준에서 유용한 리터럴을 보지 못할 것이 슬프다 (외관에서). s ~을 위한 std::string 그리고 i 상상의 단위를 위해.

UDL에 의해 저장 될 코딩 시간의 양은 실제로 그다지 높지는 않지만, 가독성이 크게 증가하고 점점 더 많은 계산이 더 빠른 실행을 위해 컴파일 타임으로 이동할 수 있습니다.

약간의 컨텍스트를 추가하겠습니다. 우리의 작업에는 사용자 정의 리터럴이 많이 필요합니다. 우리는 MDE (모델 중심 엔지니어링)에서 일합니다. C ++에서 모델과 메타 모델을 정의하려고합니다. 우리는 실제로 Ecore에서 C ++로의 매핑을 구현했습니다 (emf4cpp).

문제는 모델 요소를 C ++의 클래스로 정의 할 수있을 때 발생합니다. 우리는 메타 모델 (Ecore)을 인수와 함께 템플릿으로 변환하는 접근 방식을 취하고 있습니다. 템플릿의 인수는 유형과 클래스의 구조적 특성입니다. 예를 들어, 두 개의 int 속성이있는 클래스는 다음과 같습니다.

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

Hoever, 모델이나 메타 모델의 모든 요소는 일반적으로 이름을 가지고 있음이 밝혀졌습니다. 우리는 다음을 작성하고 싶습니다.

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

그러나 C ++ 또는 C ++ 0X는 문자열이 템플릿에 대한 인수로 금지되므로이를 허용하지 않습니다. 당신은 Char의 이름을 char로 쓸 수 있지만, 이것은 엉망입니다. 적절한 사용자 정의 리터럴을 사용하면 비슷한 것을 쓸 수 있습니다. "_n"을 사용하여 모델 요소 이름을 식별한다고 가정합니다 (정확한 구문을 사용하지 않고 아이디어를 만들기 위해) :

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

마지막으로, 이러한 정의를 템플릿으로 사용하면 유형 정보, 식별, 변환 등이 컴파일 시간의 컴파일러에 의해 결정되기 때문에 모델 요소, 모델 변환 등을 통과하기위한 알고리즘을 설계하는 데 많은 도움이됩니다.

Bjarne Stroustrup은 UDL에 대해 이야기합니다 C ++ 11 대화, 유형이 풍부한 인터페이스의 첫 번째 섹션에서 약 20 분 마크.

UDL에 대한 그의 기본 주장은 음절의 형태를 취합니다.

  1. "Trivial"유형 (즉, 기본 원시 유형)은 사소한 유형 오류 만 포착 할 수 있습니다. 유형이 풍부한 인터페이스를 사용하면 유형 시스템이 더 많은 종류의 오류를 포착 할 수 있습니다.

  2. 풍부하게 입력 한 코드가 잡을 수있는 유형 오류의 종류는 실제 코드에 영향을 미칩니다. (그는 MARS 기후 궤도의 예를 제시하며, 중요한 상수의 치수 오류로 인해 악명 높은 실패).

  3. 실제 코드에서는 단위가 거의 사용되지 않습니다. 사람들은 리치 유형을 생성하기 위해 런타임 컴퓨팅 또는 메모리 오버 헤드를 발생시키는 것이 너무 비싸기 때문에 기존 C ++ 템플릿 유닛 코드를 사용하는 것은 너무 추악하기 때문에 아무도 사용하지 않기 때문에 사람들은 사용하지 않습니다. (경험적으로, 도서관이 10 년 동안 주변에 있었음에도 불구하고 아무도 그것을 사용하지 않습니다).

  4. 따라서 엔지니어가 실제 코드에서 장치를 사용하도록하기 위해서는 (1) 런타임 오버 헤드가 발생하지 않고 (2)를 수용 할 수있는 장치가 필요했습니다.

컴파일 타임 치수 확인을 지원하는 것이 필요한 유일한 정당화입니다.

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

assert(energy == 4_J); 

예를 들어 참조하십시오 Physunits-CT-CPP11, 컴파일 타임 차원 분석 및 단위/수량 조작 및 변환을위한 작은 C ++ 11, C ++ 14 헤더 전용 라이브러리. 보다 간단합니다 부스트, 지원합니다 단위 기호 M, G, S, S, 메트릭 접두사 M, K, M과 같은 것은 표준 C ++ 라이브러리, SI-Only, 적분의 차원에 의존합니다.

흠 ...이 기능에 대해 아직 생각하지 않았습니다. 당신의 샘플은 잘 생각되었고 확실히 흥미 롭습니다. C ++는 현재와 같이 매우 강력하지만 불행히도 읽은 코드 조각에 사용되는 구문은 때때로 지나치게 복잡합니다. 가독성은 전부는 아니지만 적어도 많은 것입니다. 그리고 이러한 기능은 더욱 가독성을 위해 준비 될 것입니다. 내가 마지막 예를 들으면

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

... 오늘은 어떻게 표현하는지 궁금합니다. KG와 LB 클래스가 있고 암시 적 개체를 비교할 수 있습니다.

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

그리고 그것은 마찬가지로 그렇게 할 것입니다. 더 긴 이름이나 유형을 가진 유형이 어댑터를 작성하기에 좋은 생성자를 갖기를 희망하지 않는 유형의 경우, 즉시 암시 적 객체 생성 및 초기화에 좋은 추가 일 수 있습니다. 반면에 메소드를 사용하여 이미 객체를 생성하고 초기화 할 수 있습니다.

그러나 나는 수학에 대한 nils에 동의합니다. C 및 C ++ 삼각법 기능 예를 들어 라디안의 입력이 필요합니다. 그래도 나는 정도로 생각합니다. 그래서 Nils와 같은 매우 짧은 암시 적 변환은 매우 좋습니다.

궁극적으로, 그것은 구문 설탕이 될 것이지만, 가독성에 약간의 영향을 미칩니다. 그리고 일부 표현도 쓰는 것이 더 쉬울 것입니다 (죄 (180.0deg)는 죄보다 쓰기가 더 쉽습니다 (Deg (180.0)). 그러면 개념을 남용하는 사람들이있을 것입니다. 그러나 언어 적 부적수는 사용해야합니다. C ++처럼 표현적인 것이 아니라 매우 제한적인 언어.

아, 내 게시물은 기본적으로 아무것도 말하지 않습니다. 괜찮을 것입니다. 그 영향은 너무 크지 않을 것입니다. 걱정하지 마십시오. :-)

나는이 기능을 필요로하거나 원하지 않았다 (그러나 이것은 블루브 효과). 내 무릎 저크 반응은 그것이 절름발이이며, 오퍼레이터가 과부하가되어서 원격으로 해석 될 수있는 모든 작업에 대해 오퍼레이터를 과부하시키는 것이 시원하다고 생각하는 같은 사람들에게 호소 할 가능성이 높다는 것입니다.

C++는 일반적으로 사용되는 구문에 대해 매우 엄격합니다. 전처리기를 제외하면 사용자 정의 구문/문법을 정의하는 데 사용할 수 있는 것이 많지 않습니다.예:기존 Operato를 오버로드할 수 있지만 새로운 Operato를 정의할 수는 없습니다. IMO 이는 C++의 정신과 매우 일치합니다.

나는 좀 더 사용자 정의된 소스 코드를 위한 몇 가지 방법에는 신경 쓰지 않습니다. 그러나 선택한 요점은 나에게 매우 고립되어 있어 가장 혼란스럽습니다.

의도한 대로 사용하더라도 소스 코드를 읽기가 훨씬 더 어려워질 수 있습니다.단 하나의 편지는 문맥에서 전혀 식별할 수 없는 광범위한 부작용을 가질 수 있습니다.u, l, f에 대칭을 이루면 대부분의 개발자는 단일 문자를 선택합니다.

이것은 또한 범위 지정을 문제로 만들 수 있습니다. 전역 네임스페이스에서 단일 문자를 사용하는 것은 아마도 나쁜 습관으로 간주될 것이며 라이브러리를 더 쉽게 혼합한다고 가정되는 도구(네임스페이스 및 설명 식별자)는 아마도 그 목적을 무효화할 것입니다.

"auto"와 결합하거나 다음과 같은 단위 라이브러리와 결합하면 몇 가지 장점이 있습니다. 부스트 유닛, 그러나 이 추가를 받을 만큼 충분하지 않습니다.

그런데 과연 어떤 기발한 아이디어가 나올지 궁금합니다.

나는 다음과 같은 이진 문자열에 사용자 리터럴을 사용했습니다.

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

사용 std::string(str, n) 그렇게 만들어 져서 \0 문자열을 반으로 자르지 않을 것입니다. (프로젝트는 다양한 파일 형식으로 많은 작업을 수행합니다.)

이것은 도랑 할 때도 도움이되었습니다 std::string 래퍼를 위해 std::vector.

그 일의 라인 노이즈는 엄청납니다. 또한 읽는 것은 끔찍합니다.

알려주세요, 그들은 어떤 종류의 예제에 새로운 구문이 추가 되었습니까? 예를 들어 C ++ 0X를 이미 사용하는 몇 가지 프로그램이 있습니까?

나를 위해,이 부분 :

auto val = 3.14_i

이 부분을 정당화하지 않습니다.

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

I-Syntax를 1000 개의 다른 라인에서 사용하더라도 아닙니다. 당신이 글을 쓰면, 당신은 아마 그와 함께 다른 것들의 10000 줄을 쓸 것입니다. 특히 당신이 아마도 대부분 어디에나 글을 쓸 때 :

std::complex<double> val = 3.14i

'자동' -키워드는 아마도 정당화 될 수 있습니다. 그러나이 측면에서 C ++ 0x보다 낫기 때문에 C ++ 만 가져갑니다.

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

마치 .. 그 간단합니다. 심지어 모든 STD와 뾰족한 괄호는 어디에서나 사용한다면 절름발이라고 생각했습니다. C ++ 0X에 STD :: Complex를 복합체 아래로 돌리기 위해 어떤 구문이 있는지 추측하지 않습니다.

complex = std::complex<double>;

그것은 아마도 간단한 것이지만 C ++ 0x에서는 그렇게 간단하다고 생각하지 않습니다.

typedef std::complex<double> complex;

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

아마도? > :)

어쨌든 요점은 다음과 같습니다. std :: complex (0, 3.14) 대신 3.14i를 쓰십시오. 슈퍼 특별한 경우를 제외하고는 전반적으로 많은 시간을 절약하지 못합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top