JavaまたはC#。しかし、自分のクラスを関数std::to_string
の入力として有効にすることは可能/良い/賢明なことですか?例:
class my_class{
public:
std::string give_me_a_string_of_you() const{
return "I am " + std::to_string(i);
}
int i;
};
void main(){
my_class my_object;
std::cout<< std::to_string(my_object);
}
そのようなことがなければ(そして私はそう思う)、それを行うための最良の方法は何ですか?
まず、いくつかのADLが以下を支援します。
_namespace notstd {
namespace adl_helper {
using std::to_string;
template<class T>
std::string as_string( T&& t ) {
return to_string( std::forward<T>(t) );
}
}
template<class T>
std::string to_string( T&& t ) {
return adl_helper::as_string(std::forward<T>(t));
}
}
_
notstd::to_string(blah)
は、スコープ内に_std::to_string
_を指定してto_string(blah)
のADLルックアップを実行します。
次に、クラスを変更します。
_class my_class{
public:
friend std::string to_string(my_class const& self) const{
return "I am " + notstd::to_string(self.i);
}
int i;
};
_
notstd::to_string(my_object)
は、notstd::to_string(7)
と同様に、適切な_to_string
_を見つけます。
少し手間をかけると、自動検出および使用される型の.tostring()
メソッドもサポートできます。
「最良の」方法とは、未解決の質問です。
いくつかの方法があります。
最初に言うことは、カスタム型の_std::to_string
_のオーバーロードは許可されないであるということです。カスタムタイプのstd
名前空間では、特殊なテンプレート関数とクラスのみが許可され、_std::to_string
_はテンプレート関数ではありません。
つまり、_to_string
_を処理する良い方法は、演算子またはswap
の実装によく似ています。つまり、引数依存ルックアップが作業を行うことを許可します。
何かを文字列に変換したい場合、次のように書くことができます。
_using std::to_string;
auto s = to_string(x) + " : " + to_string(i);
_
xが名前空間YのタイプXのオブジェクトであり、iがintであると仮定すると、次のように定義できます。
_namespace Y {
std::string to_string(const X& x);
}
_
これは次のことを意味します:
to_string(x)
を呼び出すと、実際にY::to_string(const Y::X&)
が選択され、
to_string(i)
を呼び出すと、std::to_string(int)
が選択されます
さらに進んで、to_stringにoperator <<とほぼ同じことをしたい場合があります。そのため、一方を他方に関して書くことができます。
_namespace Y {
inline std::ostream& operator<<(std::ostream& os, const X& x) { /* implement here */; return os; }
inline std::string to_string(const X& x) {
std::ostringstream ss;
ss << x;
return ss.str();
}
}
_
これが代替ソリューションです。必ずしもよりエレガントで派手すぎるものはありませんが、代替アプローチです。 to_stringを呼び出すすべてのクラスにToString()関数があることを前提としています。
これは、クラスタイプのオブジェクトでのみ機能し、ToString()関数を呼び出す関数テンプレートです。
template<typename T, typename = std::enable_if_t<std::is_class<T>::value>>
std::string to_string(const T& t) {
return t.ToString();
}
たぶん、std :: stringでも動作するようにしたいかもしれません。
template<>
std::string to_string(const std::string& t) {
return t;
}
使用中のコードの例を次に示します。ダミーの名前空間to_sに注意してください。メイン関数にstd :: to_stringを使用すると、テンプレート関数名を踏むため、このように間接的に名前を導入する必要があります。誰かがこれを行う正しい方法を知っているなら、コメントをいただければ幸いです。
#include <cstring>
#include <iostream>
#include <string>
#include <type_traits>
union U {
double d;
const char* cp;
};
struct A {
enum State { kString, kDouble };
State state;
U u;
void Set(const char* cp) {
u.cp = cp;
state = kString;
}
std::string ToString() const {
switch (state) {
case A::kString : return std::string(u.cp); break;
case A::kDouble : return std::to_string(u.d); break;
default : return "Invalid State";
}
}
};
namespace to_s { using std::to_string; };
int main() {
using namespace to_s;
std::string str = "a Nice string";
double d = 1.1;
A a { A::kDouble, {1.2} };
std::cout << "str: " << to_string(str) << ", d: " << to_string(d) << std::endl;
std::cout << "a: " << to_string(a) << std::endl;
a.Set(str.c_str());
std::cout << "a: " << to_string(a) << std::endl;
std::memset(&a, 'i', sizeof(a));
std::cout << "a: " << to_string(a) << std::endl;
}
ここに私が得たものがあります:
str:ナイス文字列、d:1.100000
a:1.200000
a:素敵な文字列
a:無効な状態
独自のto_string
を独自の名前空間で定義できます(例:foo
)。
namespace foo {
std::string to_string(my_class const &obj) {
return obj.string give_me_a_string_of_you();
}
}
そしてそれを次のように使用します:
int main(){
my_class my_object;
std::cout<< foo::to_string(my_object);
}
残念ながら、標準17.6.4.2.1名前空間std [namespace.std]に準拠しているため、名前空間std
でto_string
の独自のバージョンを定義することはできません。 (エンファシス鉱山):
C++プログラムの動作は、特に指定がない限り、名前空間stdまたは名前空間std内の名前空間に宣言または定義を追加する場合、未定義です。プログラムは、標準ライブラリテンプレートのテンプレート特殊化を名前空間stdに追加できます。宣言がユーザー定義型に依存し、特殊化が元のテンプレートの標準ライブラリ要件を満たし、明示的に禁止されていない場合のみ。
これにはすでに素晴らしい答えがありますが、代替案を提案したいと思います。フィードバックを歓迎します。
To_string関数名が完全に設定されていない場合は、to_stringでサポートされる型に特化した独自のToStringフリー関数テンプレートを実装できます。
template<class T>
std::string ToString(const T& t)
{
std::ostringstream stream;
const uint8_t* pointer = &t;
for(size_t i=0;i<sizeof(T);++i)
{
stream << "0x" << std::hex << pointer[i];
}
return stream.str();
}
template<> std::string ToString(const int& t) { return std::to_string(t); }
template<> std::string ToString(const long& t) { return std::to_string(t); }
template<> std::string ToString(const long long& t) { return std::to_string(t); }
template<> std::string ToString(const unsigned& t) { return std::to_string(t); }
template<> std::string ToString(const unsigned long& t) { return std::to_string(t); }
template<> std::string ToString(const unsigned long long& t) { return std::to_string(t); }
template<> std::string ToString(const float& t) { return std::to_string(t); }
template<> std::string ToString(const double& t) { return std::to_string(t); }
ここでのデフォルトの実装は、渡されたクラス参照のメモリ空間の値とともに16進値の文字列を返しますが、特殊化はstd :: to_stringを呼び出します。これにより、すべてのクラスが「文字列化可能」になります。
次に、クラスに独自の専門化を実装する必要があります。
template<> std::string ToString(const my_class& t) { return "I am " + std::to_string(t.i); }
おそらく、次のようなoperator<<()
をオーバーロードしたいだけです。
std::ostream& operator << ( std::ostream& os, const my_class& rhs ) {
os << "I am " << rhs.i;
return os;
}
代わりに:
std::ostream& operator << ( std::ostream& os, const my_class& rhs ) {
os << rhs.print_a_string();
return os;
}
それからあなたは単にすることができます:
int main() {
my_class my_object;
std::cout << my_object;
return 0;
}
to_string
の新しいオーバーロードをstd
名前空間に追加することはできませんが、名前空間で行うことができます。
namespace my {
using std::to_string;
std::string to_string(const my_class& o) {
return o.give_me_a_string_of_you();
}
}
その後、すべてのタイプにmy::to_string
を使用できます。
int main()
{
my_class my_object;
std::cout << my::to_string(my_object);
std::cout << my::to_string(5);
}