将SFINAE上下文中不正确的模板实例化的硬错误转换为软错误

时间:2017-11-17 14:24:29

标签: c++ c++11 templates sfinae

我们说我们已经给出了模板实例化Container(认为std::vectorpush_back)和非模板类型T,我们需要检查我们是否可以在Container<T>类型的对象上调用#include <iostream> #include <vector> #include <set> #include <string> #include <type_traits> #include <boost/iterator.hpp> #include <boost/range.hpp> template<typename, typename> struct replace { using type = struct Error; }; template<template<typename...> class Container, typename U, typename T> struct replace<Container<U>, T> { using type = Container<T>; }; template<typename Container, typename T> using replace_t = typename replace<Container, T>::type; template<typename Placeholder, template<typename...> class Op, typename... Args> struct isDetected : std::false_type {}; template<template<typename...> class Op, typename... Args> struct isDetected<std::void_t<Op<Args...>>, Op, Args...> : std::true_type {}; template<typename Container, typename T> using pushBackDetector = decltype(std::declval<Container&>().push_back(std::declval<T>())); template<typename Container, typename T> bool canPushBack() { return isDetected<void, pushBackDetector, Container, T> {}; } int main() { std::cout << canPushBack<replace_t<std::vector<int>, double>, double>() << std::endl; std::cout << canPushBack<replace_t<std::set<int>, double>, double>() << std::endl; std::cout << canPushBack<replace_t<boost::iterator_range<std::string::iterator>, std::string::iterator>, double>() << std::endl; //std::cout << canPushBack<replace_t<boost::iterator_range<std::string::iterator>, int>, double>() << std::endl; } 。以下是使用检测器习语的代码:

push_back

Wandbox上的实例是available

确实,它正确地推断我们可以在std::vector<double>上调用std::set<double>,但我们无法在boost::iterator_range<std::string::iterator>push_back上执行此操作。

现在让我们检查一下我们是否可以在boost::iterator_range<int>上拨打boost::iterator_range<int>并取消注释最后一行!现在代码爆炸得非常漂亮,我不会在这里给出完整的错误消息(最好在上面链接的实例上做到这一点),但它的要点是编译器试图实例化/opt/wandbox/boost-1.65.1/clang-5.0.0/include/boost/iterator/iterator_categories.hpp:119:60: error: no type named 'iterator_category' in 'std::__1::iterator_traits<int>' typename boost::detail::iterator_traits<Iterator>::iterator_category ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~ /opt/wandbox/boost-1.65.1/clang-5.0.0/include/boost/range/iterator_range_core.hpp:156:32: note: in instantiation of template class 'boost::iterators::iterator_traversal<int>' requested here BOOST_DEDUCED_TYPENAME iterator_traversal<IteratorT>::type ^ /opt/wandbox/boost-1.65.1/clang-5.0.0/include/boost/range/iterator_range_core.hpp:436:67: note: in instantiation of template class 'boost::iterator_range_detail::pure_iterator_traversal<int>' requested here BOOST_DEDUCED_TYPENAME iterator_range_detail::pure_iterator_traversal<IteratorT>::type ^ prog.cc:31:61: note: in instantiation of template class 'boost::iterator_range<int>' requested here using pushBackDetector = decltype(std::declval<Container&>().push_back(std::declval<T>())); ^ prog.cc:28:31: note: in instantiation of template type alias 'pushBackDetector' requested here struct isDetected<std::void_t<Op<Args...>>, Op, Args...> : std::true_type {}; ^ prog.cc:36:12: note: during template argument deduction for class template partial specialization 'isDetected<std::void_t<Op<Args...> >, Op, Args...>' [with Op = pushBackDetector, Args = <boost::iterator_range<int>, double>] return isDetected<void, pushBackDetector, Container, T> {}; ^ 和将失败的一些基本类型实例化为一个硬错误:

int

一方面,这很有道理 - 事实上,false不是迭代器。另一方面,非常希望捕获这个不正确的实例化,并在这种情况下从canPushBack()返回 select `ACTUAL_TIME`, `ACTUAL_DAY`, `EXAMINED_AIRPORT_CODE`, `EXAMINED_AIRPORT_AIRPORT`, `EXAMINED_AIRPORT_COUNTRY`, `ARRIVALS/DEPARTURES`, `SCHEDULED_TIME`, `FLIGHT`, `FROM`, `AIRLANE`, `AIRCRAFT`, `STATUS`, `FLIGHT_ID_1`, `FLIGHT_ID_2`, `UPLOAD_TIME` from OSSZES as a left join ( select `ACTUAL_TIME` `ACTUAL_DAY`, COALESCE(`EXAMINED_AIRPORT_CODE`, '') as EXAMINED_AIRPORT_CODE -- edit `EXAMINED_AIRPORT_AIRPORT`, `EXAMINED_AIRPORT_COUNTRY`, `ARRIVALS/DEPARTURES`, `SCHEDULED_TIME`, `FLIGHT`, `FROM`, `AIRLANE`, `AIRCRAFT`, `STATUS`, `FLIGHT_ID_1`, `FLIGHT_ID_2`, `UPLOAD_TIME` from OSSZES_CLEAR ) as b using (`ACTUAL_DAY`,`EXAMINED_AIRPORT_CODE`,`FLIGHT`,`SCHEDULED_TIME`,`STATUS`) where b.EXAMINED_AIRPORT_CODE is null into outfile '/tmp/proba.csv' fields terminated by ';' enclosed by '"' lines terminated by '\n'; 。所以,这就是问题:是否有可能将这个硬错误变成软错误并优雅地处理它?<​​/ p>

1 个答案:

答案 0 :(得分:7)

不,您不能采用不支持SFINAE检测的模板,并且不需要针对相关类型的手动工作使SFINAE友好,有时候还不够。

你能做的最好的事情就是写一个为你做的手动特性,以及一个SFINAE检查是否可以应用的别名,如果可以的话只返回一个类型。

此外,检测某些东西是否为迭代器是不可能的。没有标准规定的SFINAE友好型&#34;是X迭代器&#34;试验。作为一般规则,所有迭代器都必须支持std::iterator_traits<T>,但是有要求非迭代器在将它们传递给std::iterator_traits时必须生成SFINAE友好结果,并且我将void*传递给std::iterator_traits的经历会产生非SFINAE友好的结果。

您可以尝试破解一个 - 检测迭代器必须执行的各种事情(可解除引用,可递增,同等比较),但即使有类型在您尝试时也可能没有SFINAE友好错误。例如,采用非同等比较的类型并将其放在std::vector中,并且尝试执行==可能无法使用硬错误进行编译(至少上次检查时)。

一个简单的例子是:

template<class T>
struct problem {
  static_assert(!std::is_same<T,int>{}, "oh oh");
};

int传递给problem无法将SFINAE检测为问题。如果您实例化problem<int>,则会出现硬错误。