주로 파이썬 배경에서 나오기 때문에 C ++에서 유형 작업에 다소 어려움을 겪었습니다.
다른 유형을 매개 변수로 사용하는 여러 오버로드 된 생성자 중 하나를 통해 클래스 변수를 초기화하려고합니다. auto
키워드 를 사용하여 변수의 자동 선언에 사용할 수 있지만 내 경우에는 생성자를 선택할 때까지 초기화되지 않습니다. 그러나 컴파일러는 초기화하지 않는 것에 만족하지 않습니다 value
.
class Token {
public:
auto value;
Token(int ivalue) {
value = ivalue;
}
Token(float fvalue) {
value = fvalue;
}
Token(std::string svalue) {
value = svalue;
}
void printValue() {
std::cout << "The token value is: " << value << std::endl;
}
};
파이썬에서는 다음과 같이 보일 수 있습니다.
class Token():
def __init__(self, value):
self.value = value
def printValue(self):
print("The token value is: %s" % self.value)
auto
이 시나리오 에서 키워드 를 사용하는 올바른 방법은 무엇입니까 ? 다른 접근법을 함께 사용해야합니까?
답변
C ++에서 오버로드 된 생성자를 통해 알 수없는 유형의 변수 초기화
C ++에는 “알 수없는 유형의 변수”와 같은 것은 없습니다.
이 시나리오에서 자동 키워드를 사용하는 올바른 방법은 무엇입니까?
자동 삭감 된 변수에는 초 기자에서 추론되는 유형이 있습니다. 이니셜 라이저가 없으면 auto를 사용할 수 없습니다. 비 정적 멤버 변수에는 auto를 사용할 수 없습니다. 클래스의 한 인스턴스는 다른 인스턴스와 다른 유형의 멤버를 가질 수 없습니다.
이 시나리오에서는 자동 키워드를 사용하는 방법이 없습니다.
다른 접근법을 함께 사용해야합니까?
아마. 구현하려고하는 것 같습니다 std::variant
. X 숫자 유형 중 하나를 저장하는 변수가 필요한 경우 사용해야합니다.
그러나 C ++에서 동적 입력을 모방하려고 할 수 있습니다. 파이썬에 대한 경험으로 인해 친숙 할 수도 있지만 대부분의 경우 이상적인 접근 방식은 아닙니다. 예를 들어,이 특정 예제 프로그램에서는 멤버 변수로 수행하는 모든 것이 인쇄됩니다. 따라서 각 경우에 문자열을 저장하는 것이 더 간단합니다. 다른 접근법은 Fire Lancer가 보여주는 Rhathin 또는 OOP 스타일의 동적 다형성 으로 표시되는 정적 다형성 입니다.
답변
C ++는 정적으로 유형이 지정된 언어 이므로 모든 변수 유형이 런타임 전에 결정됩니다. 따라서 auto
키워드는var
동적으로 입력되는 언어 인 javascript의 키워드와 다릅니다. auto
키워드는 일반적으로 불필요하게 복잡한 유형을 지정하는 데 사용됩니다.
찾고있는 것은 대신 C ++ 템플릿 클래스를 사용하여 수행 할 수 있으므로 다른 유형의 클래스를 여러 버전으로 만들 수 있습니다.
이 코드는 당신이 찾고있는 대답 일 수 있습니다.
template <typename T>
class Token {
private:
T value;
public:
Token(const T& ivalue) {
value = ivalue;
}
void printValue() {
std::cout << "The token value is: " << value << std::endl;
}
};
이 코드는 operator<<
std :: ostream & 및 T 유형에 대해 함수 를 정의해야하는 것처럼 일부 조건이 충족되면 컴파일됩니다 .
답변
다른 사람들이 제안한 것과 다른 접근법은 템플릿을 사용하는 것입니다. 예를 들면 다음과 같습니다.
template<class T>
class Token {
public:
T value;
Token(T value) :
value(std::move(value))
{}
void printValue() {
std::cout << "The token value is: " << value << std::endl;
}
};
그런 다음 수업을 다음과 같이 사용할 수 있습니다.
Token<int> x(5);
x.printValue();
답변
std::variant
타입을 사용할 수 있습니다 . 아래 코드는 한 가지 방법을 보여줍니다 (그러나 약간 어색합니다, 인정해야합니다).
#include <iostream>
#include <variant>
class Token {
public:
std::variant<int, float, std::string> value;
Token(int ivalue) {
value = ivalue;
}
Token(float fvalue) {
value = fvalue;
}
Token(std::string svalue) {
value = svalue;
}
void printValue() {
switch (value.index()) {
case 0:
std::cout << "The token value is: " << std::get<0>(value) << std::endl;
break;
case 1:
std::cout << "The token value is: " << std::get<1>(value) << std::endl;
break;
case 2:
std::cout << "The token value is: " << std::get<2>(value) << std::endl;
break;
}
}
};
int main() {
Token it(1);
Token ft(2.2f);
Token st("three");
it.printValue();
ft.printValue();
st.printValue();
return 0;
}
“x” 는 컴파일 타임 상수 표현식이어야합니다. std::get<0>(value)
로 작성 될 수 있다면 훨씬 좋을 것 입니다.std::get<value.index()>(value)
<x>
답변
auto
특정 유형으로 추론 가능해야하며 런타임 동적 입력을 제공하지 않습니다.
선언 Token
할 때 사용할 수있는 모든 가능한 유형을 알고 std::variant<Type1, Type2, Type3>
있다면 “type enum”과 “union”을 갖는 것과 비슷합니다. 적절한 생성자와 소멸자가 호출되도록합니다.
std::variant<int, std::string> v;
v = "example";
v.index(); // 1, a int would be 0
std::holds_alternative<std::string>(v); // true
std::holds_alternative<int>(v); // false
std::get<std::string>(v); // "example"
std::get<int>(v); // throws std::bad_variant_access
Token
적절한 가상 방법을 사용하여 각 사례마다 다른 하위 유형 (템플릿 사용 가능) 을 만들 수도 있습니다 .
class Token {
public:
virtual void printValue()=0;
};
class IntToken : public Token {
public:
int value;
IntToken(int ivalue) {
value = ivalue;
}
virtual void printValue()override
{
std::cout << "The token value is: " << value << std::endl;
}
}
답변
아래 솔루션은 Fire Lancer의 답변과 비슷합니다. 중요한 차이점은 템플릿을 사용하여 주석을 따르 므로 인터페이스의 파생 인스턴스를 명시 적으로 만들 필요 가 없다는 것 입니다. Token
그 자체가 인터페이스 클래스가 아닙니다. 대신, 인터페이스를 내부 클래스로 정의하고 파생 클래스의 정의를 자동화하기위한 내부 템플리트 클래스를 정의합니다.
정의가 지나치게 복잡해 보입니다. 그러나 Token::Base
인터페이스를 정의하고 인터페이스에서 Token::Impl<>
파생됩니다. 이 내부 클래스는의 사용자에게 완전히 숨겨져 Token
있습니다. 사용법은 다음과 같습니다.
Token s = std::string("hello");
Token i = 7;
std::cout << "The token value is: " << s << '\n';
std::cout << "The token value is: " << i << '\n';
또한 아래 솔루션은 변환 연산자를 구현하여 Token
인스턴스를 일반 변수 에 할당하는 방법을 보여줍니다 . 에 의존 dynamic_cast
하고 캐스트가 유효하지 않은 경우 예외가 발생합니다.
int j = i; // Allowed
int k = s; // Throws std::bad_cast
정의 Token
는 다음과 같습니다.
class Token {
struct Base {
virtual ~Base () = default;
virtual std::ostream & output (std::ostream &os) = 0;
};
template <typename T>
struct Impl : Base {
T val_;
Impl (T v) : val_(v) {}
operator T () { return val_; }
std::ostream & output (std::ostream &os) { return os << val_; }
};
mutable std::unique_ptr<Base> impl_;
public:
template <typename T>
Token (T v) : impl_(std::make_unique<Impl<T>>(v)) {}
template <typename T>
operator T () const { return dynamic_cast<Impl<T>&>(*impl_); }
friend auto & operator << (std::ostream &os, const Token &t) {
return t.impl_->output(os);
}
};