対応するto_string()
の型を見たことがありますが、オーバーロードされていませんoperator<<()
。したがって、ストリームに挿入するときには、<< to_string(x)
が冗長である必要があります。サポートされている場合はユーザoperator<<()
という汎用関数を記述することが可能かどうかは疑問ですが、そうでない場合は<< to_string()
になります。operator <<()が失敗したときのto_string()へのフォールバック
答えて
を試してみてください。
トリックはoperator<<
は、利用可能であることを確認することです必ずしも型定義によって提供される1:
namespace helper {
template<typename T> std::ostream& operator<<(std::ostream& os, T const& t)
{
return os << to_string(t);
}
}
using helper::operator<<;
std::cout << myFoo;
このトリックは、一般的にstd::swap<T>
の間で選択する必要があり、一般的なコードで使用されており、特化したFoo::swap(Foo::Bar&, Foo::Bar&)
です。
はい、可能です。
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>
struct streamy
{
};
std::ostream&
operator<<(std::ostream& os, const streamy& obj)
{
return os << "streamy [" << static_cast<const void *>(&obj) << "]";
}
struct stringy
{
};
std::string
to_string(const stringy& obj)
{
auto oss = std::ostringstream {};
oss << "stringy [" << static_cast<const void *>(&obj) << "]";
return oss.str();
}
template <typename T>
std::enable_if_t
<
std::is_same
<
std::string,
decltype(to_string(std::declval<const T&>()))
>::value,
std::ostream
>&
operator<<(std::ostream& os, const T& obj)
{
return os << to_string(obj);
}
int
main()
{
std::cout << streamy {} << '\n';
std::cout << stringy {} << '\n';
}
ジェネリックoperator<<
表現to_string(obj)
がobj
const T&
のためによく型付けされ、タイプstd::string
の結果を持っている場合にのみ使用可能になります。あなたのコメントであなたがすでに推測しているように、これは実際にSFINAEの職場です。 decltype
の式が整形式でないと、置換に失敗し、オーバーロードが消滅します。
しかし、これによりあいまいなオーバーロードで問題が発生する可能性があります。少なくともoperator<<
を独自のnamespace
に置き、必要に応じてusing
宣言を介してローカルにドラッグするだけです。私はあなたが同じことをする名前付き関数を書く方が良いと思う。
namespace detail
{
enum class out_methods { directly, to_string, member_str, not_at_all };
template <out_methods> struct tag {};
template <typename T>
void
out(std::ostream& os, const T& arg, const tag<out_methods::directly>)
{
os << arg;
}
template <typename T>
void
out(std::ostream& os, const T& arg, const tag<out_methods::to_string>)
{
os << to_string(arg);
}
template <typename T>
void
out(std::ostream& os, const T& arg, const tag<out_methods::member_str>)
{
os << arg.str();
}
template <typename T>
void
out(std::ostream&, const T&, const tag<out_methods::not_at_all>)
{
// This function will never be called but we provide it anyway such that
// we get better error messages.
throw std::logic_error {};
}
template <typename T, typename = void>
struct can_directly : std::false_type {};
template <typename T>
struct can_directly
<
T,
decltype((void) (std::declval<std::ostream&>() << std::declval<const T&>()))
> : std::true_type {};
template <typename T, typename = void>
struct can_to_string : std::false_type {};
template <typename T>
struct can_to_string
<
T,
decltype((void) (std::declval<std::ostream&>() << to_string(std::declval<const T&>())))
> : std::true_type {};
template <typename T, typename = void>
struct can_member_str : std::false_type {};
template <typename T>
struct can_member_str
<
T,
decltype((void) (std::declval<std::ostream&>() << std::declval<const T&>().str()))
> : std::true_type {};
template <typename T>
constexpr out_methods
decide_how() noexcept
{
if (can_directly<T>::value)
return out_methods::directly;
else if (can_to_string<T>::value)
return out_methods::to_string;
else if (can_member_str<T>::value)
return out_methods::member_str;
else
return out_methods::not_at_all;
}
template <typename T>
void
out(std::ostream& os, const T& arg)
{
constexpr auto how = decide_how<T>();
static_assert(how != out_methods::not_at_all, "cannot format type");
out(os, arg, tag<how> {});
}
}
template <typename... Ts>
void
out(std::ostream& os, const Ts&... args)
{
const int dummy[] = {0, ((void) detail::out(os, args), 0)...};
(void) dummy;
}
そして、そのようにそれを使用しています。
int
main()
{
std::ostringstream nl {"\n"}; // has `str` member
out(std::cout, streamy {}, nl, stringy {}, '\n');
}
機能decide_how
は、利用可能な複数のオプションがある場合でも、あなたに出力する方法を指定された型を決定する際に、完全な柔軟性を提供します。拡張も容易です。たとえば、ADLで検索可能なto_string
フリー関数ではなく、str
メンバ関数を持つタイプがあります。 (実際には、すでにそれを行っています)
関数detail::out
は、tag dispatchingを使用して適切な出力方法を選択します。
can_HOW
述語は、私が非常にエレガントなvoid_t
trickを使用して実装されています。
variadic out
関数は“for each argument” trickを使用していますが、これはさらに洗練されています。
コードはC++ 14であり、最新のコンパイラが必要です。
これはSFINAEですか?したがって 'to_string(x)'がコンパイルされるとき、 'return os << to_string(obj);'オーバーロードが存在し、それ以外の場合は存在しませんか? 'std :: conditional'の代わりに' std :: enable_if'を使うことができますか? – Lingxi
'<< x'がコンパイルされない場合、オーバーロードが発生すると、' << to_string(x) 'オーバーロードが発生するといいでしょう。 – Lingxi
はい、これはSFINAEです。更新された答えを見てください(特に、2番目のコメントに返信してください)。私は 'std :: enable_if'を使うことを考えましたが、簡単な解決策を見つけることができなかったので、かなり混乱している' std :: conditional'と一緒に行きました。 – 5gon12eder
は、ADLを使用し、SFINAEが過剰です
template <typename T>
void print_out(T t) {
print_out_impl(std::cout, t, 0);
}
template <typename OS, typename T>
void print_out_impl(OS& o, T t,
typename std::decay<decltype(
std::declval<OS&>() << std::declval<T>()
)>::type*) {
o << t;
}
template <typename OS, typename T>
void print_out_impl(OS& o, T t, ...) {
o << t.to_string();
}
@MSalters(クレジットは彼に行く)の答えに基づいて、この1つは私の問題を解決し、完全な答えを出すべきです。
#include <iostream>
#include <string>
#include <type_traits>
struct foo_t {};
std::string to_string(foo_t) {
return "foo_t";
}
template <class CharT, class Traits, class T>
typename std::enable_if<std::is_same<CharT, char>::value,
std::basic_ostream<CharT, Traits>&>::type
operator<<(std::basic_ostream<CharT, Traits>& os, const T& x) {
return os << to_string(x);
}
int main() {
std::cout << std::string{"123"} << std::endl;
std::cout << foo_t{} << std::endl;
}
- 1. HystrixRunTimeException:<operation>タイムアウトとフォールバックに失敗しました
- 2. std :: operator <<とstd :: ostream :: operator <<
- 3. operator <<のネームスペースのプライベートクラスへのアクセス
- 4. バーチャル<< operator
- 5. ostream :: operator <<はshared_ptrのoperator->で動作しません
- 6. `<= ':Fixnumと配列の比較が失敗しました(ArgumentError)
- 7. operator <<をメンバー関数としてオーバーロードできません
- 8. << operator with char with pointer
- 9. "cout << '\\\\';"というステートメントはなぜ失敗しないのですか?
- 10. SQLAgentのジョブpowershell手順が失敗しました。 "<DOMAIN> $ <COMPUTER> $"
- 11. CDNが失敗した場合のjquery-toolsフォールバック
- 12. popper.jsがロードに失敗した場合のフォールバック
- 13. NAntでの<mbunit>の使用が失敗しました
- 14. 安全なルックアップに失敗した場合のデフォルト値へのフォールバック方法
- 15. std :: map <tstring <std :: map <tstring、unsigned int >>割り当てが失敗しました
- 16. は、失敗した<IEnumerableを<T>>()
- 17. 失敗しました:</p> <p>失敗しました:エラー処理文書#1:mongoimport
- 18. ERROR:</p> <p>:デバイスへの展開に失敗しました、Visual Studioの
- 19. 匿名オブジェクトのoperator <<を使用できません
- 20. 失敗/エラー:response.should render_template(新しい)<"new">を期待したが<"">
- 21. URLError:<urlopenエラー[errnoが11001]失敗したのgetaddrinfo>
- 22. GNUのreverse_iterator <Iterator> :: operator->とプロキシイテレータ
- 23. HTML5キャッシュマニフェスト:フォールバックの取得に失敗したAjax呼び出し
- 24. QStringのstd :: ostreamとoperator <<を継承するクラス
- 25. to_latex()(TO_STRING機能はありません)の<code>to_string()</code>からパラメータ
- 26. operator <<にconstが必要なのはなぜですか?
- 27. アップロードに失敗しました:\ <filename.sql>をs3:// <bucket-name>/<filename.sql> [SSL:CERTIFICATE_VERIFY_FAILED]証明書の検証に失敗しました(_ssl.c:661)
- 28. PyTorch RuntimeError:アサーション `cur_target> = 0 && cur_target <n_classes 'が失敗しました
- 29. AuthorizationException:<例外str()が失敗しました> Scan on
- 30. <FIRAnalytics/ERROR>トランザクションが失敗しました
これは、 'namespace'または' to_string(T) 'で定義された' T'型に対して 'operator <<'だけを必要とする場合にはもっと簡単であることに同意します。尋ねられたので、+1。あなたがさらにディスパッチする必要がある場合、これは動作しません。また、このソリューションによって生成されるエラーメッセージは、可能な限り役立たない可能性があります。 – 5gon12eder
これはいいですね。しかし、 'to_string()'だけをオーバーロードした型ごとに 'operator <<()'をオーバーロードしなければなりません。私はそのような退屈な仕事を避けたい。 – Lingxi
何を?なぜあなたはそれをしなければならないと思いますか? – Yakk