web-dev-qa-db-ja.com

カスタム型のstd :: to_stringの特殊化はC ++標準で許可されていますか?

C++ 11以降では、std名前空間のstd::to_stringをカスタム型に特化することはできますか?

namespace std {
string to_string(::MyClass const & c) { return c.toString(); }
}

ユースケースの例:

int main() {
    MyClass c;
    std::cout << std::to_string(c) << std::endl;
}
36
jotik

C++ 11以降では、std :: to_stringをstd名前空間でカスタム型に特化することはできますか?

いいえ、まず第一に、 それはテンプレート関数 ではないので、それをまったく専門化することはできません。

独自のオーバーロード関数の追加について質問している場合でも、答えは同じです。

Extending the namespace std のドキュメントスニペット:

宣言または定義を名前空間stdまたはstd内にネストされた任意の名前空間に追加するのは未定義の動作ですが、以下に示すいくつかの例外があります

標準ライブラリテンプレートのテンプレート特殊化を名前空間stdに追加できるのは、宣言がユーザー定義型に依存し、その特殊化が禁止されている場合を除き、特殊化が元のテンプレートのすべての要件を満たしている場合のみです。


実際には、すべてがおそらくうまく機能しますが、厳密に言えば、標準は、何が起こるかについての保証はないと述べています。


編集:私は公式の標準にアクセスできないので、以下は 自由作業草案(N4296) からのものです:

17.6.4.2名前空間の使用

17.6.4.2.1名前空間std

  1. 特に明記されていない限り、宣言または定義を名前空間stdまたは名前空間std内の名前空間に追加する場合、C++プログラムの動作は未定義です。宣言がユーザー定義型に依存し、特殊化が元のテンプレートの標準ライブラリ要件を満たし、明示的に禁止されていない場合にのみ、プログラムは標準ライブラリテンプレートの特殊化テンプレートを名前空間stdに追加できます。181
  2. 宣言した場合のC++プログラムの動作は未定義です。

    2.1 —標準ライブラリクラステンプレートのメンバー関数の明示的な特殊化、または

    2.2 —標準ライブラリクラスまたはクラステンプレートのメンバー関数テンプレートの明示的な特殊化、または

    2.3 —標準ライブラリクラスまたはクラステンプレートのメンバークラステンプレートの明示的または部分的な特殊化。

    プログラムは、宣言がユーザー定義型の名前に依存し、インスタンス化が元のテンプレートの標準ライブラリ要件を満たしている場合にのみ、標準ライブラリで定義されたテンプレートを明示的にインスタンス化できます。

  3. 翻訳単位は、名前空間stdをインライン名前空間として宣言することはできません(7.3.1)。
33
James Adkison

私が間違っていない場合は、単純にto_stringをジェネリック型にオーバーロードできます。

template<typename T> to_string(const T& _x) {
    return _x.toString();
}

これにより、プログラムでADL(引数依存ルックアップ)を使用して、渡されたタイプに基づいて関連するto_stringメソッドを正しく選択できます。

12
sjrowlinson

より良い方法は、可能であれば_std::to_string_を使用する独自の関数を作成することです。また、渡された引数に使用できる場合は常に.toString()メソッドを使用します。

_#include <type_traits>
#include <iostream>
#include <string>

struct MyClass {
   std::string toString() const { return "MyClass"; }
};

template<class T>
typename std::enable_if<std::is_same<decltype(std::declval<const T&>().toString()), std::string>::value, std::string>::type my_to_string(const T &t) {
    return t.toString();
}

template<class T>
typename std::enable_if<std::is_same<decltype(std::to_string(std::declval<T&>())), std::string>::value, std::string>::type my_to_string(const T &t) {
    return std::to_string(t);
}

int main() {
   std::cout << my_to_string(MyClass()) << std::endl; // will invoke .toString
   std::cout << my_to_string(1) << std::endl; //will invoke std::to_string
}
_
8
W.F.

C++ 11以降では、std :: to_stringをstd名前空間でカスタム型に特化することはできますか?

いいえ、to_string()std名前空間にオーバーロードを追加することはできません。

良いニュースはする必要がないということです。簡単な解決策があります!

独自の実装を提供し、ADL(引数依存ルックアップ)に問題を解決させることができます。

方法は次のとおりです。

_class A {};

std::string to_string(const A&)
{
    return "A()";
}

int main()
{
    A a;
    using std::to_string;
    std::cout << to_string(2) << ' ' << to_string(a);
}
_

ここでは sing宣言 を使用して_std::to_string_をスコープに移動し、次にto_string()への非修飾呼び出しを使用しました。

これで_std::to_string_と_::to_string_の両方が表示され、コンパイラは適切なオーバーロードを選択します。

毎回_using std::to_string_を使用する前に_to_string_を記述したくない場合、または名前空間なしで_to_string_を使用するのを忘れる場合は、ヘルパー関数を作成できます。

_template<typename T>
std::string my_to_string(T&& t)
{
    using std::to_string;
    return to_string(std::forward<T>(t));
}
_

この関数は任意の名前空間で定義でき、クラスが定義されている名前空間とは独立して機能します(同じである必要はありません)。

を参照してください。

[〜#〜]注[〜#〜]:これは、_to_string_を呼び出している場合に機能します。 _std::to_string_を呼び出すライブラリーがあり、タイプに合わせて変更したい場合は、うまくいきません。

4
Makers_F