枚举和静态常量成员变量在模板特征中的使用 class
enum and static const member variable usage in template trait class
我想通过查看是否提供了 operator<<
的重载来测试 class 是否可以流式传输到 ostream&
。基于these posts,我尝试用C++11写了另一个版本。这是我的尝试:
#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
和 bool
有这么大的区别?
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<<
需要bool
正好:#6。也就是说,即使 bool
,你的特征仍然不正确,因为它总是 returns false
。您的 NotDefined
版本实际上是 returns 一个参考,因此您必须核对一下。而且,NotDefined&
表示 未定义 ,因此您必须翻转符号:
static const bool value = !std::is_same<decltype(s << t), NotDefined&>();
然而,这特别容易出错。现在 cout << B{};
不是编译失败而是给你一个链接器错误,而 cout << B{} << endl;
给你同样令人困惑的重载错误涉及 endl
而不是简单地声明你不能流式传输 B
。
你应该更喜欢这样做:
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct stream_insertion_exists : std::false_type { };
template <typename T>
struct stream_insertion_exists<T, void_t<
decltype(std::declval<std::ostream&>() << std::declval<T>())
> > : std::true_type { };
我想通过查看是否提供了 operator<<
的重载来测试 class 是否可以流式传输到 ostream&
。基于these posts,我尝试用C++11写了另一个版本。这是我的尝试:
#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
和 bool
有这么大的区别?
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<<
需要bool
正好:#6。也就是说,即使 bool
,你的特征仍然不正确,因为它总是 returns false
。您的 NotDefined
版本实际上是 returns 一个参考,因此您必须核对一下。而且,NotDefined&
表示 未定义 ,因此您必须翻转符号:
static const bool value = !std::is_same<decltype(s << t), NotDefined&>();
然而,这特别容易出错。现在 cout << B{};
不是编译失败而是给你一个链接器错误,而 cout << B{} << endl;
给你同样令人困惑的重载错误涉及 endl
而不是简单地声明你不能流式传输 B
。
你应该更喜欢这样做:
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct stream_insertion_exists : std::false_type { };
template <typename T>
struct stream_insertion_exists<T, void_t<
decltype(std::declval<std::ostream&>() << std::declval<T>())
> > : std::true_type { };