正如Xeo说,使其工作,必须#include <iterator>
中的begin
适当的定义带来的。更确切地说,这个工程:
#include <iterator>
#include <utility>
template <class T> constexpr auto
std_begin_callable (T const*) -> decltype (std::begin (std::declval <T>()), bool())
{ return true; }
template <class> constexpr bool
std_begin_callable (...)
{ return false; }
#include <array>
static_assert (std_begin_callable <std::array <int, 3>> (0), "failed");
int main() {}
现在,让我们来看看为什么原来的代码不包括<iterator>
编译,但是没有得到预期的结果(除非你移动#include <array>
了)。
包含<utility>
间接意味着包含<initializer_list>
,其定义为std::begin(std::initializer_list<T>)
。因此,在此翻译单元中,名称std::begin
可见。
但是,当您拨打std_begin_callable
时,第一个过载被SFINAEd带走,因为可见std::begin
不能接受std::array
。现在
,如果您删除的<iterator>
和<utility>
共夹杂物(保持<array>
std_begin_callable
后),然后编译失败,因为编译器将不再看到std::begin
或std::declval
任何过载:
template <class T> constexpr auto
std_begin_callable (T const*) -> decltype (std::begin (std::declval <T>()), bool())
{ return true; } // error: begin/declval is not a member of std
template <class> constexpr bool
std_begin_callable (...)
{ return false; }
#include <array>
static_assert (std_begin_callable <std::array <int, 3>> (0), "failed");
int main() {}
最后,可以复制/简化与此以前的错误行为:
namespace std {
void begin();
template <typename T>
T&& declval();
}
template <class T> constexpr auto
std_begin_callable (T const*) -> decltype (std::begin (std::declval <T>()), bool())
{ return true; } // No compiler error here, just SFINAE.
template <class> constexpr bool
std_begin_callable (...)
{ return false; }
#include <array>
static_assert (std_begin_callable <std::array <int, 3>> (0), "failed");
int main() {}
UPD吃了:
从评论(这里和在OP)我想这是不可能解决你想要的方式头文件顺序问题。那么,让我建议的基础上,ADL一个变通方法,是接近的解决方案,以及可能(但可能没有),配不上你的使用情况:
// <your_header_file>
#include <iterator>
#include <utility>
namespace detail {
using std::begin;
template <typename T, typename = decltype(begin(*((T*)0)))>
constexpr std::true_type std_begin_callable(int) { return std::true_type(); }
template <typename>
constexpr std::false_type std_begin_callable(long) { return std::false_type(); };
};
template <typename T>
constexpr auto std_begin_callable() ->
decltype(detail::std_begin_callable<typename std::remove_reference<T>::type>(0)) {
return detail::std_begin_callable<typename std::remove_reference<T>::type>(0);
}
// </your_header_file>
// <a_supposedly_std_header_file>
namespace std {
struct foo { int begin() /* const */; };
struct bar;
int begin(/*const*/ bar&);
template <typename T> struct goo;
template <typename T>
int begin(/*const*/ goo<T>&);
}
// </a_supposedly_std_header_file>
// <a_3rd_party_header_file>
namespace ns {
struct foo { int begin() /*const*/; };
struct bar;
int begin(/*const*/ bar&);
template <typename T> struct goo;
template <typename T>
int begin(/*const*/ goo<T>&);
}
// </a_3rd_party_header_file>
//<some_tests>
static_assert (std_begin_callable</*const*/ std::foo>(), "failed");
static_assert (std_begin_callable</*const*/ std::bar>(), "failed");
static_assert (std_begin_callable</*const*/ std::goo<int>>(), "failed");
static_assert (std_begin_callable</*const*/ ns::foo>(), "failed");
static_assert (std_begin_callable</*const*/ ns::bar>(), "failed");
static_assert (std_begin_callable</*const*/ ns::goo<int>>(), "failed");
//</some_tests>
int main() {}
这似乎工作,但我还没有完全测试。我建议你尝试用/几种组合没有注释掉const
S IN的代码。
我用*((T*)0)
而不是std::declval<T>()
,因为一个常量问题。要看到它,请将declval
退回,然后尝试static_assert
替代const ns::foo
,并将ns::foo::begin
退出非const
。
这是未定义的行为,在模板已经用于其他地方之后向模板添加专业化。 – 2013-04-23 08:38:01
@KerrekSB:你确定在这种情况下是相关的吗?毕竟,会导致隐式实例化的第一次使用是在'static_assert' – 2013-04-23 08:42:16
@AndyProwl:我很喜欢90%左右。我相信这已经在之前讨论过了,这就是我们得出的结论。当然,我可能是错的。 – 2013-04-23 08:44:41