Как преобразовать что-либо в строку неявно?
Моя цель - разработать класс String, который декорирует std::string для того, чтобы обеспечить некоторую функциональность, необходимую моей программе. Одна функциональность, которую я хочу добавить, - это возможность неявно преобразовывать что-либо в мою строку, чтобы сэкономить время при наборе текста.
Чтобы достичь неявного преобразования, я разработал следующий класс:
std::ostream& operator<<(std::ostream& o, const String& s);
class String {
public:
template<typename t_value>
String::String(t_value value) {
std::ostringstream oss;
oss << value;
_str = oss.str();
}
private:
std::string _str;
}
Это прекрасно работает с любым типом, который имеет <<
оператор определен. Проблема возникла с любым классом, у которого нет оператора потока. Ошибка компилятора была бы в порядке, но я получил рекурсию бесконечности, так как C++ пытается использовать мой глобальный <<
оператор, чтобы попытаться преобразовать в мой тип String.
Моя основная задача - написать код
class Foo {
int _memberWithUnderscoreInName;
}
String s = Foo();
И получить ошибку компилятора вместо бесконечного цикла в конструкторе.
Есть ли простое решение для этого?
2 ответа
Вместо объявления оператора вывода в окружающем пространстве имен, объявите его только как друга String
учебный класс:
class String {
public:
// This must be implemented inline here
friend std::ostream& operator<<(std::ostream& o, const String& s) {
return o << _str; // for example
}
template<typename t_value>
String(t_value value) {
std::ostringstream oss;
oss << value;
_str = oss.str();
}
private:
std::string _str;
};
Теперь он может быть найден только путем поиска, зависящего от аргумента, и поэтому будет рассматриваться только в том случае, если второй аргумент действительно имеет тип String
, а не просто конвертируется в него. Таким образом, он не будет рассматриваться в качестве кандидата на os << value
в конструкторе, давая ошибку компиляции, а не смертельную спираль во время выполнения, если нет другого кандидата.
Это или подобное должно исправить это:
namespace HasFormattedOutput {
namespace Detail
{
struct Failure{};
}
template<typename OutputStream, typename T>
Detail::Failure operator << (OutputStream&, const T&);
template<typename OutputStream, typename T>
struct Result : std::integral_constant<
bool,
! std::is_same<
decltype((*(OutputStream*)0) << std::declval<T>()),
Detail::Failure
>::value
> {};
} // namespace HasFormattedOutput
template <typename T, typename OutputStream = std::ostream>
struct has_formatted_output
: HasFormattedOutput::Result<OutputStream, T>
{};
class X {
public:
X() {}
template <typename T>
X(const T& t) {
static_assert(
has_formatted_output<T>::value,
"Not supported type.");
std::ostringstream s;
s << t;
str = s.str();
}
private:
std::string str;
};
std::ostream& operator << (std::ostream& stream, const X&) { return stream; }
struct Y {
Y() {}
};
int main() {
Y y;
X x(y);
return 0;
}