模板特征类中的枚举和静态const成员变量用法

时间:2015-05-16 15:21:51

标签: c++ templates c++11 overload-resolution

我想通过查看是否提供ii的重载来测试类是否可以流式传输到ostream&。基于these posts,我尝试使用C ++ 11编写另一个版本。这是我的尝试:

operator<<

但这无法编译:

#include <iostream>
#include <type_traits>

namespace TEST{
  class NotDefined{};

  template<typename T> 
  NotDefined& operator << (::std::ostream&, const T&);

  template <typename T>
  struct StreamInsertionExists {
    static std::ostream &s;
    static T const &t;
    enum { value = std::is_same<decltype(s << t), NotDefined>() };
  };
}

struct A{
  int val;
    friend ::std::ostream& operator<<(::std::ostream&, const A&);
};

::std::ostream& operator<<(::std::ostream& os, const A& a)
{
  os << a.val;
  return os;
}

struct B{};

int main() {
  std::cout << TEST::StreamInsertionExists<A>::value << std::endl;
  std::cout << TEST::StreamInsertionExists<B>::value << std::endl;

}

但是,如果我更换线路
test_oper.cpp:40:57: error: reference to overloaded function could not be resolved; did you mean to call it? std::cout << TEST::StreamInsertionExists<A>::value << std::endl; /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ostream:1020:1: note: possible target for call endl(basic_ostream<_CharT, _Traits>& __os) test_oper.cpp:30:17: note: candidate function not viable: no known conversion from 'TEST::NotDefined' to '::std::ostream &' (aka 'basic_ostream<char> &') for 1st argument ::std::ostream& operator<<(::std::ostream& os, const A& a) test_oper.cpp:15:15: note: candidate template ignored: couldn't infer template argument 'T' NotDefined& operator << (::std::ostream&, const T&);

enum { value = std::is_same<decltype(s << t), NotDefined>() };
那么一切都会编译。

为什么static const bool value = std::is_same<decltype(s << t), NotDefined>();enum之间存在这样的差异?

2 个答案:

答案 0 :(得分:4)

\x9d(\xe7\xae\xbc@\x94x\x0b\xa0*\xd8\x93\ value中匿名名称的枚举。当你尝试这样做时:

StreamInsertionExists<T>

编译器正在std::cout << StreamInsertionExists<T>::value; 上进行重载查找。在典型情况下,它会对operator<<(std::ostream&, StreamInsertionExists<T>::E)进行整体提升,并将其作为enum进行流式处理。但是,您还定义了此运算符:

int

这是template<typename T> NotDefined& operator << (std::ostream&, const T&); enum版本(完全匹配与整数提升)更好的匹配,所以它是首选。是的,它是一个功能模板,但只有转换序列匹配时才会使用非模板 - 在这种情况下,它们不会。

因此,这一行:

int

是:

std::cout << TEST::StreamInsertionExists<A>::value << std::endl;

最里面的operator<<(operator<<(std::cout, TEST::StreamInsertionExists<A>::value), std::endl); 来电将使用您的operator<<模板。因此,下一步是找到适当的函数调用:

NotDefined&

并且operator<<(NotDefined&, std::endl); 没有这样的重载因此错误。

如果您将operator<<更改为value,则不存在此问题,因为 bool完全取消operator<<:{ {3}}。也就是说,即使使用bool,您的特征仍然不正确,因为它始终返回bool。您的false版本实际上会返回引用,因此您必须对此进行检查。此外,NotDefined表示定义,因此您必须翻转标志:

NotDefined&

然而,这特别容易出错。现在static const bool value = !std::is_same<decltype(s << t), NotDefined&>(); 而不是无法编译会改为给你一个链接器错误,而cout << B{};会给你带来与cout << B{} << endl;相同的令人困惑的重载错误,而不是简单地说明你不能流一个endl

你应该更喜欢这样做:

B

答案 1 :(得分:1)

出于某种原因,我不能让Barry的解决方案与VS2013(vc120 pack 3)一起工作,替换失败不会发生,所以stream_insertion_exists :: value总是返回true(任何想法为什么呢?)

根据Metaprograming: Failure of Function Definition Defines a Separate Function,这是一个解决方法:

template <typename T>
auto has_stream_insertion_helper( ... ) // ... to disambiguate call
    -> std::false_type;

template <typename T>
auto has_stream_insertion_helper( int ) // int to disambiguate call
    -> decltype( std::declval<std::ostream&>() << std::declval<T>(), std::true_type{} );

template <typename T>
using has_stream_insertion = decltype( has_stream_insertion_helper<T>( 0 ) );

class ClassNoStream {};
class ClassHasStream {};
std::ostream&   operator<<( std::ostream&, const ClassHasStream& );

static_assert( has_stream_insertion< int >::value, "" );
static_assert( !has_stream_insertion< ClassNoStream >::value, "" );
static_assert( has_stream_insertion< ClassHasStream >::value, "" );