使用 SFINAE 检查全局运算符<<?

发布于 2024-11-03 05:24:42 字数 3013 浏览 0 评论 0 原文

我想要几个重载的全局 to_string() 函数,它们接受某种类型 T 并将其转换为其字符串表示形式。对于一般情况,我希望能够编写:

template<typename T,class OutputStringType> inline
typename enable_if<!std::is_pointer<T>::value
                && has_insertion_operator<T>::value,
                   void>::type
to_string( T const &t, OutputStringType *out ) {
  std::ostringstream o;
  o << t;
  *out = o.str();
}

到目前为止,我的 has_insertion_operator 实现是:(

struct sfinae_base {
  typedef char yes[1];
  typedef char no[2];
};

template<typename T>
struct has_insertion_operator : sfinae_base {
  template<typename U> static yes& test( U& );
  template<typename U> static no& test(...);

  static std::ostream &s;
  static T const &t;

  static bool const value = sizeof( test( s << t ) ) == sizeof( yes ); // line 48
};

它借用了 这个这个。) 这似乎有效。 但现在我想要一个 to_string 的重载版本,用于那些没有operator<<的类型> 有自己的 to_string() member 函数,即:

template<class T,class OutputStringType> inline
typename enable_if<!has_insertion_operator<T>::value
                && has_to_string<T,std::string (T::*)() const>::value,
                   void>::type
to_string( T const &t, OutputStringType *out ) {
  *out = t.to_string();
}

has_to_string 的实现是:(

#define DECL_HAS_MEM_FN(FN_NAME)                                      \
  template<typename T,typename S>                                     \
  struct has_##FN_NAME : sfinae_base {                                \
    template<typename SignatureType,SignatureType> struct type_check; \
    template<class U> static yes& test(type_check<S,&U::FN_NAME>*);   \
    template<class U> static no& test(...);                           \
    static bool const value = sizeof( test<T>(0) ) == sizeof( yes );  \
  }

DECL_HAS_MEM_FN( to_string );

这部分看起来工作正常。它改编自<一href="https://stackoverflow.com/questions/257288/">这个。) 然而,当我:

struct S {
  string to_string() const {
    return "42";
  }
};

int main() {
  string buf;
  S s;
  to_string( s, &buf ); // line 104
}

我得到:

foo.cpp: In instantiation of ‘const bool has_insertion_operator<S>::value’:
foo.cpp:104:   instantiated from here
foo.cpp:48: error: no match for ‘operator<<’ in ‘has_insertion_operator<S>::s << has_insertion_operator<S>::t’

SFINAE 似乎没有发生。如何正确编写 has_insertion_operator 以便确定全局 operator<< 是否可用?

仅供参考:我正在使用 g++ 4.2.1(作为 Mac OS X 上 Xcode 的一部分提供)。 另外,我希望代码只是标准的 C++03,没有第 3 方库,例如 Boost。

谢谢!

I want to have several overloaded, global to_string() functions that take some type T and convert it to its string representation. For the general case, I want to be able to write:

template<typename T,class OutputStringType> inline
typename enable_if<!std::is_pointer<T>::value
                && has_insertion_operator<T>::value,
                   void>::type
to_string( T const &t, OutputStringType *out ) {
  std::ostringstream o;
  o << t;
  *out = o.str();
}

My implementation of has_insertion_operator so far is:

struct sfinae_base {
  typedef char yes[1];
  typedef char no[2];
};

template<typename T>
struct has_insertion_operator : sfinae_base {
  template<typename U> static yes& test( U& );
  template<typename U> static no& test(...);

  static std::ostream &s;
  static T const &t;

  static bool const value = sizeof( test( s << t ) ) == sizeof( yes ); // line 48
};

(It borrows from this
and this.)
That seems to work.
But now I want to have an overloaded version of to_string for types that do not have operator<< but do have their own to_string() member function, i.e.:

template<class T,class OutputStringType> inline
typename enable_if<!has_insertion_operator<T>::value
                && has_to_string<T,std::string (T::*)() const>::value,
                   void>::type
to_string( T const &t, OutputStringType *out ) {
  *out = t.to_string();
}

The implementation of has_to_string is:

#define DECL_HAS_MEM_FN(FN_NAME)                                      \
  template<typename T,typename S>                                     \
  struct has_##FN_NAME : sfinae_base {                                \
    template<typename SignatureType,SignatureType> struct type_check; \
    template<class U> static yes& test(type_check<S,&U::FN_NAME>*);   \
    template<class U> static no& test(...);                           \
    static bool const value = sizeof( test<T>(0) ) == sizeof( yes );  \
  }

DECL_HAS_MEM_FN( to_string );

(This part seems to work fine. It's adapted from this.)
However, when I have:

struct S {
  string to_string() const {
    return "42";
  }
};

int main() {
  string buf;
  S s;
  to_string( s, &buf ); // line 104
}

I get:

foo.cpp: In instantiation of ‘const bool has_insertion_operator<S>::value’:
foo.cpp:104:   instantiated from here
foo.cpp:48: error: no match for ‘operator<<’ in ‘has_insertion_operator<S>::s << has_insertion_operator<S>::t’

It seems like SFINAE is not happening. How do I write has_insertion_operator correctly such that it determines whether a global operator<< is available?

FYI: I'm using g++ 4.2.1 (that which ships as part of Xcode on Mac OS X).
Also, I'd like the code to be only standard C++03 without 3rd-party libraries, e.g., Boost.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

裂开嘴轻声笑有多痛 2024-11-10 05:24:42

我应该更忠实于这个答案。
一个可行的实现是:

namespace has_insertion_operator_impl {
  typedef char no;
  typedef char yes[2];

  struct any_t {
    template<typename T> any_t( T const& );
  };

  no operator<<( std::ostream const&, any_t const& );

  yes& test( std::ostream& );
  no test( no );

  template<typename T>
  struct has_insertion_operator {
    static std::ostream &s;
    static T const &t;
    static bool const value = sizeof( test(s << t) ) == sizeof( yes );
  };
}

template<typename T>
struct has_insertion_operator :
  has_insertion_operator_impl::has_insertion_operator<T> {
};

我相信它实际上并不依赖于 SFINAE。

I should have simply been more faithful to this answer.
A working implementation is:

namespace has_insertion_operator_impl {
  typedef char no;
  typedef char yes[2];

  struct any_t {
    template<typename T> any_t( T const& );
  };

  no operator<<( std::ostream const&, any_t const& );

  yes& test( std::ostream& );
  no test( no );

  template<typename T>
  struct has_insertion_operator {
    static std::ostream &s;
    static T const &t;
    static bool const value = sizeof( test(s << t) ) == sizeof( yes );
  };
}

template<typename T>
struct has_insertion_operator :
  has_insertion_operator_impl::has_insertion_operator<T> {
};

I believe that it does not actually rely on SFINAE.

罪#恶を代价 2024-11-10 05:24:42

第 48 行 value 的初始值设定项不在 SFINAE 工作的上下文中。尝试将表达式移动到函数声明中。

#include <iostream>

struct sfinae_base {
  typedef char yes[1];
  typedef char no[2];
};

template<typename T>
struct has_insertion_operator : sfinae_base {

  // this may quietly fail:
  template<typename U> static yes& test(
      size_t (*n)[ sizeof( std::cout << * static_cast<U*>(0) ) ] );

  // "..." provides fallback in case above fails
  template<typename U> static no& test(...);

  static bool const value = sizeof( test<T>( NULL ) ) == sizeof( yes );
};

然而,我不得不质疑这件事的复杂程度。我看到非正交机制会相互冲突(to_stringoperator<<),并且我听到一些糟糕的假设被抛弃(例如 运算符<< 是全局的,而不是成员,尽管在这方面实现的代码看起来不错)。

The initializer of value on line 48 is not in a context where SFINAE works. Try moving the expression to the function declaration.

#include <iostream>

struct sfinae_base {
  typedef char yes[1];
  typedef char no[2];
};

template<typename T>
struct has_insertion_operator : sfinae_base {

  // this may quietly fail:
  template<typename U> static yes& test(
      size_t (*n)[ sizeof( std::cout << * static_cast<U*>(0) ) ] );

  // "..." provides fallback in case above fails
  template<typename U> static no& test(...);

  static bool const value = sizeof( test<T>( NULL ) ) == sizeof( yes );
};

However, I have to question the amount of sophistication that is going into this. I see non-orthogonal mechanisms that will grind against each other (to_string vs. operator<<) and I hear poor assumptions getting tossed around (for example that operator<< is global vs a member, although the code as implemented looks OK in that regard).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文