正如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>
which 定义std::begin(std::initializer_list<T>)
。因此,在此翻译单元中,名称std::begin
是可见的。
但是,当您调用std_begin_callable
第一个重载时,SFINAEd 消失了,因为可见std::begin
不能采用std::array
.
现在,如果您完全删除<iterator>
and的包含<utility>
(保留<array>
在 之后std_begin_callable
),那么编译将失败,因为编译器将不再看到std::begin
or的任何重载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 () {}
更新:
从评论(此处和 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
的几种组合。
我使用*((T*)0)
而不是std::declval<T>()
因为一个常量问题。要看到它,放回去,declval
然后尝试static_assert
离开非。const ns::foo
ns::foo::begin
const