0xd*_*00d 6 c++ templates sfinae c++11
假设我们给出了一个模板实例化Container<U, Args...>
(想想Container
是一个std::vector
)和一个非模板类型T,我们需要检查是否可以调用push_back
一个类型的对象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;
}
Run Code Online (Sandbox Code Playgroud)
一个活生生的例子是可在Wandbox.
事实上,正确的推论,我们可以调用push_back
上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> {};
^
Run Code Online (Sandbox Code Playgroud)
一方面,这很有道理 - 事实上,int
它不是一个迭代器.在另一方面,这是非常希望抓住这个不正确的实例化,就回到false
从canPushBack()
在这种情况下.所以,这就是问题:是否有可能将这个硬错误变成软错误并优雅地处理它?
不,您不能采用不支持SFINAE检测的模板,并且在没有特定类型的手动工作的情况下使SFINAE友好,有时这还不够.
您可以做的最好的事情是编写一个为您执行此操作的手动特征,以及SFINAE检查是否可以应用它的别名,并且只返回类型(如果可以).
此外,无法检测某些东西是否是迭代器.没有标准规定的SFINAE友好的"是X迭代器"测试.作为一般规则,所有迭代器必须支持std::iterator_traits<T>
,但也有为零,当你把它们传递给非迭代器必须产生一个SFINAE友好的结果的要求std::iterator_traits
,在我过往的经验void*
,以std::iterator_traits
产生非SFINAE友好的结果.
您可以尝试破解一个 - 检测迭代器必须执行的各种事情(可解除引用,可递增,同等比较),但即使有类型也可能在尝试时没有SFINAE友好错误.例如,采用非同等比较类型并将其放入a中std::vector
,并且尝试执行操作==
可能无法使用硬错误进行编译(至少上次检查时).
一个简单的例子是:
template<class T>
struct problem {
static_assert(!std::is_same<T,int>{}, "oh oh");
};
Run Code Online (Sandbox Code Playgroud)
传递int
到problem
不能被SFINAE检测为一个问题.如果您实例化problem<int>
,则会出现硬错误.
归档时间: |
|
查看次数: |
292 次 |
最近记录: |