c ++ SFINAE - 带省略号的回退重载不起作用
c++ SFINAE - fallback overload with ellipsis does not work
我正在编写使用具有迭代器的 STL 容器的函数。
我正在尝试处理没有的容器。
我的模板函数:
template <typename T>
void easyfind(...)
{
throw std::invalid_argument("No iterator");
}
template <typename T>
typename T::iterator easyfind(T& cont, int tofind)
{
typename T::iterator iter;
iter = std::find(cont.begin(), cont.end(), tofind);
if (iter == cont.end())
throw std::out_of_range("Cannot find");
return iter;
}
main.cpp:
// @ list
{
std::list<int> L;
std::list<int>::iterator iter;
L.push_back(5);
L.push_back(6);
L.push_back(7);
try
{
iter = easyfind(L, 7);
std::cout << "found " << *iter << std::endl;
}
catch (std::exception& e)
{
std::cout << e.what() << std::endl;
}
}
// @ stack ( no iterator )
{
std::stack<int> S;
S.push(10);
S.push(11);
S.push(12);
try
{
easyfind(S, 12); // expect fallback case
}
catch (std::exception& e)
{
std::cout << e.what() << std::endl;
}
}
我原以为带堆栈的 easyfind 会调用 void easyfind(...)
,但是:
main.cpp:88:4: error: no matching function for call to 'easyfind'
easyfind(S, 12);
^~~~~~~~
./easyfind.hpp:21:6: note: candidate template ignored: couldn't infer template argument 'T'
void easyfind(...);
^
./easyfind.hpp:27:22: note: candidate template ignored: substitution failure [with T = std::stack<int>]: no type named 'iterator' in 'std::stack<int>'
typename T::iterator easyfind(T& cont, int tofind);
第二个忽略是我所期望的,但我不明白为什么它不能调用回退函数。我错过了什么?
T
未用作函数参数,因此无法推断出 T
应该是什么。在这种情况下,您可以用可变参数模板替换可变参数函数:
template <class... Args>
void easyfind(Args&&...) // Now Args... can be deduced
{
throw std::invalid_argument("No iterator");
}
但是,您当前的检查排除了普通数组,例如 int A[3];
,并且它还会在运行时抛出异常,而不是在编译时生成明确的错误消息。您可以添加类型特征来检查 std::begin(container)
是否有效。
#include <iterator>
#include <type_traits>
template<class T>
struct has_iterator {
static std::false_type test(...); // fallback
template<class U> // matches if std::begin() is valid
static auto test(const U& u) -> decltype(std::begin(u), std::true_type{});
static constexpr bool value = decltype(test(std::declval<T>()))::value;
};
template<class T>
inline constexpr bool has_iterator_v = has_iterator<T>::value;
您的 easyfind
然后可以在 static_assert
中使用该类型特征,而不是添加模板重载并在运行时抛出。
template <class T, class F>
auto easyfind(T&& cont, F&& tofind) {
// emit a clear compile time error message about the problem:
static_assert(has_iterator_v<T>, "No iterator");
auto iter = std::find(std::begin(cont), std::end(cont), std::forward<F>(tofind));
if (iter == std::end(cont)) throw std::out_of_range("Cannot find");
return iter;
}
我正在编写使用具有迭代器的 STL 容器的函数。
我正在尝试处理没有的容器。
我的模板函数:
template <typename T>
void easyfind(...)
{
throw std::invalid_argument("No iterator");
}
template <typename T>
typename T::iterator easyfind(T& cont, int tofind)
{
typename T::iterator iter;
iter = std::find(cont.begin(), cont.end(), tofind);
if (iter == cont.end())
throw std::out_of_range("Cannot find");
return iter;
}
main.cpp:
// @ list
{
std::list<int> L;
std::list<int>::iterator iter;
L.push_back(5);
L.push_back(6);
L.push_back(7);
try
{
iter = easyfind(L, 7);
std::cout << "found " << *iter << std::endl;
}
catch (std::exception& e)
{
std::cout << e.what() << std::endl;
}
}
// @ stack ( no iterator )
{
std::stack<int> S;
S.push(10);
S.push(11);
S.push(12);
try
{
easyfind(S, 12); // expect fallback case
}
catch (std::exception& e)
{
std::cout << e.what() << std::endl;
}
}
我原以为带堆栈的 easyfind 会调用 void easyfind(...)
,但是:
main.cpp:88:4: error: no matching function for call to 'easyfind'
easyfind(S, 12);
^~~~~~~~
./easyfind.hpp:21:6: note: candidate template ignored: couldn't infer template argument 'T'
void easyfind(...);
^
./easyfind.hpp:27:22: note: candidate template ignored: substitution failure [with T = std::stack<int>]: no type named 'iterator' in 'std::stack<int>'
typename T::iterator easyfind(T& cont, int tofind);
第二个忽略是我所期望的,但我不明白为什么它不能调用回退函数。我错过了什么?
T
未用作函数参数,因此无法推断出 T
应该是什么。在这种情况下,您可以用可变参数模板替换可变参数函数:
template <class... Args>
void easyfind(Args&&...) // Now Args... can be deduced
{
throw std::invalid_argument("No iterator");
}
但是,您当前的检查排除了普通数组,例如 int A[3];
,并且它还会在运行时抛出异常,而不是在编译时生成明确的错误消息。您可以添加类型特征来检查 std::begin(container)
是否有效。
#include <iterator>
#include <type_traits>
template<class T>
struct has_iterator {
static std::false_type test(...); // fallback
template<class U> // matches if std::begin() is valid
static auto test(const U& u) -> decltype(std::begin(u), std::true_type{});
static constexpr bool value = decltype(test(std::declval<T>()))::value;
};
template<class T>
inline constexpr bool has_iterator_v = has_iterator<T>::value;
您的 easyfind
然后可以在 static_assert
中使用该类型特征,而不是添加模板重载并在运行时抛出。
template <class T, class F>
auto easyfind(T&& cont, F&& tofind) {
// emit a clear compile time error message about the problem:
static_assert(has_iterator_v<T>, "No iterator");
auto iter = std::find(std::begin(cont), std::end(cont), std::forward<F>(tofind));
if (iter == std::end(cont)) throw std::out_of_range("Cannot find");
return iter;
}