C ++에서 템플릿 친화적 인 문자열을 숫자로


48

C ++ 표준 라이브러리에는 문자열에서 숫자 유형으로 변환하는 함수가 있습니다.

stoi
stol
stoll
stoul
stoull
stof
stod
stold

그러나 템플릿 코드에서 사용하는 것이 지루하다고 생각합니다. 다음과 같은 템플릿 기능이없는 이유 :

template<typename T>
T sto(...)

문자열을 숫자 형식으로 변환하는 방법?

나는 기술적 인 이유가 보이지 않지만 뭔가 빠진 것 같습니다. 기본 명명 된 함수를 호출하고 enable_if/ concepts를 사용하여 숫자가 아닌 유형을 비활성화 하도록 특수화 할 수 있습니다 .

표준 라이브러리에 문자열을 숫자 형식으로 변환하고 다른 방법으로 효율적으로 변환하는 템플릿 친화적 인 대안이 있습니까?


이것이 귀하의 질문에 대답합니까? `std :: sto` ... 시리즈가 왜 템플릿이 아닌가?
Boiethios

1
@Boiethios는 실제로는 아닙니다. 그 질문에 대한 답변은 "이유"에 대한 이론적 근거를 설명하지만 허용되는 답변과 같은 실용적인 솔루션은 제공되지 않습니다.
Mircea Ispas

답변:


40

다음과 같은 템플릿 기능이없는 이유 :

C ++ 17에는 일반적인 문자열 대 숫자 기능이 있지만 이름이 다릅니다. 그들은 std::from_chars모든 숫자 유형에 대해 오버로드 된 과 함께 사용 되었습니다.

보시다시피, 첫 번째 과부하는 정수 유형을 출력 매개 변수로 사용하고 가능한 경우 값을 할당합니다.

다음과 같이 사용할 수 있습니다 :

template<typename Numeric>
void stuff(std::string_view s) {
    auto value = Numeric{};

    auto [ptr, error] = std::from_chars(s.data(), s.data() + s.size(), value);

    if (error) {
        // error with the conversion
    } else {
        // conversion successful, do stuff with value
    }
}

보시다시피 일반적인 컨텍스트에서 작동 할 수 있습니다.


5
C ++은 이제 파괴적인가? : o 구조적 바인딩 선언
Alexander-Reinstate Monica

1
물론이야! 간단한 구조체 또는 올바른 인터페이스가 제공되면 클래스도 사용할 수 있습니다.
기 illa 라치 콧

13

템플릿이 아니며 로케일에서 작동하지 않지만 쇼 스토퍼가 아닌 경우 C ++ 17에는 이미 원하는 것이 있습니다. std::from_chars

모든 정수 및 부동 소수점 유형에 대한 과부하가 있으며 정수 및 부동 소수점 유형에 대해 각각 다른 마지막 매개 변수를 제외하고 인터페이스는 동일합니다 (그러나 기본값이 양호하면 다음을 수행 할 필요가 없습니다) 변경). 이것은 로케일 인식 기능이 아니기 때문에 매우 빠릅니다. 다른 문자열 대 값 변환 기능을 능가하며 일반적으로 수십 배입니다.

Stephan T. Lavavej의 CPPCON 비디오 <charconv>는 헤더 from_chars의 사용법과 성능을 확인할 수 있습니다. https://www.youtube.com/watch?v=4P_kbF0EbZM


1
@NathanOliver : stoi및 그 친구 (질문에 언급 된 변환)도 로케일에서 작동하지 않으므로 쇼 토퍼가 아닙니다.
피트 베커

9

다음과 같은 표현으로 인해 많이 얻지 못할 것입니다.

int x = sto("1");

템플릿 매개 변수에 대해 원하는 유형을 추론 할 수있는 쉬운 방법이 없습니다. 당신은 작성해야

int x = sto<int>("1");

어느 정도까지는 일반적인 기능을 제공한다는 목적을 상실합니다. 반면에

template<typename T>
void sto(std::string x,T& t);

당신이 깨달은대로 잘 사용됩니다. C ++ 17에는 std::from_chars(거의 템플릿이 아니라 과부하 세트이며 문자열 대신 문자에 대한 포인터가 필요하지만 사소한 세부 사항 만 있습니다) 거의 또는 거의 수행하는.

추신 : 위의 표현에서 원하는 유형을 추론하는 쉬운 방법은 없지만 방법이 있습니다. 나는 당신의 질문의 핵심이 정확히 당신이 요구 한 서명이라고 생각하지 않으며, 다음은 그것을 구현하는 좋은 방법이라고 생각하지 int x = sto("1");않습니다. 행동.

#include <iostream>
#include <string>

struct converter {
    const std::string& x;
    template <typename T> operator T() { return 0;}
};

template <> converter::operator int() { return stoi(x); }
template <> converter::operator double() { return stod(x); }
converter sto(const std::string& x) { return {x}; }

int main() {
    std::string s{"1.23"};
    int x = sto(s);
    double y = sto(s);
    std::cout << x << " " << y;
}

이것은 의도 한대로 작동하지만 심각한 단점이있을 수 있습니다. 가장 중요한 것은 쓰기가 가능합니다 auto x = sto(s);. 즉, 잘못 사용하기 쉽습니다.


여기서 암묵적인 변환에 의존하는 것이 좋습니다. 자동을 비활성화하려고 시도하는 것은 문제입니다. 일반적으로 개인 메서드 참조를 유효한 메서드로만 초기화되는 클래스에 넣는 것으로 나타났습니다. 진행하기 전에 전체 변환기 객체를 구성해야하기 때문에 여기서 어떻게 활용하는지 알 수 없습니다. 흠 ..
bremen_matt

교육되지 않은 유형 매개 변수에도 불구하고 가치를 볼 수 있습니다. 질문에서 알 수 있듯이 동기 부여는 템플릿 코드 내에서 사용할 수 있어야합니다. 여기서 인스턴스화마다 다른 유형으로 변환합니다.
Toby Speight

근본적인 문제는 auto x = sto(s)무엇입니까? 이 특정 구현 converter::x은 범위를 벗어난 참조 이기 때문에 중단 되지만 수정할 수는 있습니다. 참조를 제거하고 std::string의 이동 의미 에 의존하십시오 .
MSalters

@MSalters 예, 문제가 있다고 생각되는 참조였습니다.하지만 옳습니다. 참조를 사용할 필요가 없습니다. 실제로 나를 더 혼란스럽게 만드는 것은 함수 인 것처럼 보이지만 실제 기능은입니다 converter. 템플릿 변환 연산자를 사용하는 것이 최선의 선택인지 확실하지 않습니다. 어쩌면 내가 처음 생각했던 것처럼 나쁘지 않을 수도 있습니다
idclev 463035818

const 참조에 아무런 문제가 없다고 생각합니다. 내 이해는 const 참조가 변환기가 파괴 될 때까지 문자열의 수명을 보존한다는 것입니다 ( herbsutter.com/2008/01/01/… )
bremen_matt


3

이전 C ++ 버전에서는 stringstream이 친구입니다. 내가 올바르게 이해하면 다음이 흥미로울 것입니다. C ++ 11입니다.

https://wandbox.org/permlink/nUNiUwWWTr7a0NXM

#include <sstream>
#include <string>
#include <iostream>

template<typename T, typename String>
T sto(const String & str) {
    T val;
    std::stringstream ss(str);
    ss >> val;
    return val;
}

template<typename T, typename String>
void sto(const String & str, T & val) {
    std::stringstream ss(str);
    ss >> val;
}

int main() {   
    std::cout << sto<float>("1.1") << ", " << sto<int>(std::string{"2"});

    // An alternative version that infers the type 
    double d;
    sto("3.3", d);
    std::cout << ", " << d;
}

이 방법은 C ++ 11에서 작동하며 꽤 일반적입니다. 내 경험상이 방법은 강력하지만 성능이 가장 우수하지는 않습니다.


예, 이것이 제가 사용한 것입니다. 그러나 성능은 때때로 바람직하지 않은 이름이 붙은 기능들입니다.
Mircea Ispas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.