void_t“可以实现概念”?
我正在观看Walter Brown的CppCon2014谈话模板元编程的第二部分,在此期间他讨论了他的小说void_t<>构造的void_t<> 。 在他的演讲中,彼得·索莫拉德问他一个我不太明白的问题。  (链接直接回答问题,正在讨论的代码直接发生在那之前) 
Sommerlad问道
沃尔特,这是否意味着我们现在可以实现概念lite?
沃尔特回应
哦耶! 我已经完成了…它没有完全相同的语法。
 我理解这个交stream是关于概念精简版。 这种模式真的是多才多艺的? 不pipe出于什么原因,我都没有看到它。 有人可以解释(或草图)这样的事情吗? 这仅仅是关于enable_if和定义特征,或者提问者提到的是什么? 
  void_t模板定义如下: 
 template<class ...> using void_t = void; 
 然后他使用它来检测types语句是否格式正确,使用它来实现is_copy_assignabletypes特征: 
 //helper type template<class T> using copy_assignment_t = decltype(declval<T&>() = declval<T const&>()); //base case template template<class T, class=void> struct is_copy_assignable : std::false_type {}; //SFINAE version only for types where copy_assignment_t<T> is well-formed. template<class T> struct is_copy_assignable<T, void_t<copy_assignment_t<T>>> : std::is_same<copy_assignment_t<T>,T&> {}; 
由于谈话,我明白这个例子是如何工作的,但是我不明白我们从这里得到像Lite这样的东西。
 是的,概念lite基本上装扮SFINAE。 此外,它允许更深入的反思,以便更好的超载。 然而,只有在概念谓词被定义为concept bool时才有效。 改进的重载不适用于当前的概念谓词,但可以使用条件重载。 让我们看看如何在C ++ 14中定义谓词,约束模板和重载函数。 这是很长的,但它覆盖了如何创build在C ++ 14中完成这些所需的所有工具。 
定义谓词
 首先,用所有的std::declval和decltype处处读取谓词是很难看的。 相反,我们可以利用这个事实,即我们可以用一个尾随的decltype来限制一个函数(这里是Eric Niebler的博客文章),如下所示: 
 struct Incrementable { template<class T> auto requires_(T&& x) -> decltype(++x); }; 
 所以,如果++x无效, requires_成员函数就不可调用。 所以我们可以创build一个models特征来检查void_t是否可以使用void_t来调用: 
 template<class Concept, class Enable=void> struct models : std::false_type {}; template<class Concept, class... Ts> struct models<Concept(Ts...), void_t< decltype(std::declval<Concept>().requires_(std::declval<Ts>()...)) >> : std::true_type {}; 
约束模板
 所以当我们想要基于这个概念约束模板时,我们仍然需要使用enable_if ,但是我们可以使用这个macros来帮助它变得更加清晰: 
 #define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0 
 所以我们可以定义一个基于Incrementable概念约束的increment函数: 
 template<class T, REQUIRES(models<Incrementable(T)>())> void increment(T& x) { ++x; } 
 所以如果我们用increment的东西来调用Incrementable ,我们会得到这样的错误: 
 test.cpp:23:5: error: no matching function for call to 'incrementable' incrementable(f); ^~~~~~~~~~~~~ test.cpp:11:19: note: candidate template ignored: disabled by 'enable_if' [with T = foo] template<class T, REQUIRES(models<Incrementable(T)>())> ^ 
重载函数
 现在,如果我们要重载,我们要使用条件重载。 假设我们要使用概念谓词创build一个std::advance ,我们可以像这样定义它(现在我们将忽略可递减的情况): 
 struct Incrementable { template<class T> auto requires_(T&& x) -> decltype(++x); }; struct Advanceable { template<class T, class I> auto requires_(T&& x, I&& i) -> decltype(x += i); }; template<class Iterator, REQUIRES(models<Advanceable(Iterator, int)>())> void advance(Iterator& it, int n) { it += n; } template<class Iterator, REQUIRES(models<Incrementable(Iterator)>())> void advance(Iterator& it, int n) { while (n--) ++it; } 
 然而,这会导致一个模糊的重载(在概念lite中,当它和std::vector迭代器一起使用时,这仍然是一个模糊的重载,除非我们改变谓词来引用一个concept bool的其他谓词)。 我们要做的就是调用我们可以使用条件重载的方法。 它可以被认为是写这样的东西(这是不正确的C + +): 
 template<class Iterator> void advance(Iterator& it, int n) if (models<Advanceable(Iterator, int)>()) { it += n; } else if (models<Incrementable(Iterator)>()) { while (n--) ++it; } 
 所以如果第一个函数没有被调用,它会调用下一个函数。 所以让我们开始实现它的两个function。 我们将创build一个名为basic_conditional的类,它接受两个函数对象作为模板参数: 
 struct Callable { template<class F, class... Ts> auto requires_(F&& f, Ts&&... xs) -> decltype( f(std::forward<Ts>(xs)...) ); }; template<class F1, class F2> struct basic_conditional { // We don't need to use a requires clause here because the trailing // `decltype` will constrain the template for us. template<class... Ts> auto operator()(Ts&&... xs) -> decltype(F1()(std::forward<Ts>(xs)...)) { return F1()(std::forward<Ts>(xs)...); } // Here we add a requires clause to make this function callable only if // `F1` is not callable. template<class... Ts, REQUIRES(!models<Callable(F1, Ts&&...)>())> auto operator()(Ts&&... xs) -> decltype(F2()(std::forward<Ts>(xs)...)) { return F2()(std::forward<Ts>(xs)...); } }; 
所以现在这意味着我们需要将我们的函数定义为函数对象:
 struct advance_advanceable { template<class Iterator, REQUIRES(models<Advanceable(Iterator, int)>())> void operator()(Iterator& it, int n) const { it += n; } }; struct advance_incrementable { template<class Iterator, REQUIRES(models<Incrementable(Iterator)>())> void operator()(Iterator& it, int n) const { while (n--) ++it; } }; static conditional<advance_advanceable, advance_incrementable> advance = {}; 
 所以现在如果我们尝试使用它与一个std::vector : 
 std::vector<int> v = { 1, 2, 3, 4, 5, 6 }; auto iterator = v.begin(); advance(iterator, 4); std::cout << *iterator << std::endl; 
 它将编译和打印出5 。 
 但是, std::advance实际上有三个重载,所以我们可以使用basic_conditional来实现conditional ,这个conditional可以用于recursion的任意数量的函数: 
 template<class F, class... Fs> struct conditional : basic_conditional<F, conditional<Fs...>> {}; template<class F> struct conditional<F> : F {}; 
 所以,现在我们可以像这样编写完整的std::advance : 
 struct Incrementable { template<class T> auto requires_(T&& x) -> decltype(++x); }; struct Decrementable { template<class T> auto requires_(T&& x) -> decltype(--x); }; struct Advanceable { template<class T, class I> auto requires_(T&& x, I&& i) -> decltype(x += i); }; struct advance_advanceable { template<class Iterator, REQUIRES(models<Advanceable(Iterator, int)>())> void operator()(Iterator& it, int n) const { it += n; } }; struct advance_decrementable { template<class Iterator, REQUIRES(models<Decrementable(Iterator)>())> void operator()(Iterator& it, int n) const { if (n > 0) while (n--) ++it; else { n *= -1; while (n--) --it; } } }; struct advance_incrementable { template<class Iterator, REQUIRES(models<Incrementable(Iterator)>())> void operator()(Iterator& it, int n) const { while (n--) ++it; } }; static conditional<advance_advanceable, advance_decrementable, advance_incrementable> advance = {}; 
超载与兰姆达斯
 不过,另外,我们可以使用lambdas来代替函数对象,这样可以使得它更易于编写。 所以我们使用这个STATIC_LAMBDAmacros在编译时构造lambdaexpression式: 
 struct wrapper_factor { template<class F> constexpr wrapper<F> operator += (F*) { return {}; } }; struct addr_add { template<class T> friend typename std::remove_reference<T>::type *operator+(addr_add, T &&t) { return &t; } }; #define STATIC_LAMBDA wrapper_factor() += true ? nullptr : addr_add() + [] 
 并添加一个make_conditional函数是constexpr : 
 template<class... Fs> constexpr conditional<Fs...> make_conditional(Fs...) { return {}; } 
 那么我们现在可以这样写advancefunction: 
 constexpr const advance = make_conditional( STATIC_LAMBDA(auto& it, int n, REQUIRES(models<Advanceable(decltype(it), int)>())) { it += n; }, STATIC_LAMBDA(auto& it, int n, REQUIRES(models<Decrementable(decltype(it))>())) { if (n > 0) while (n--) ++it; else { n *= -1; while (n--) --it; } }, STATIC_LAMBDA(auto& it, int n, REQUIRES(models<Incrementable(decltype(it))>())) { while (n--) ++it; } ); 
这比使用函数对象版本更紧凑和可读。
 另外,我们可以定义一个modeled函数来减lessdecltype ugliness: 
 template<class Concept, class... Ts> constexpr auto modeled(Ts&&...) { return models<Concept(Ts...)>(); } constexpr const advance = make_conditional( STATIC_LAMBDA(auto& it, int n, REQUIRES(modeled<Advanceable>(it, n))) { it += n; }, STATIC_LAMBDA(auto& it, int n, REQUIRES(modeled<Decrementable>(it))) { if (n > 0) while (n--) ++it; else { n *= -1; while (n--) --it; } }, STATIC_LAMBDA(auto& it, int n, REQUIRES(modeled<Incrementable>(it))) { while (n--) ++it; } ); 
最后,如果你有兴趣使用现有的图书馆解决scheme(而不是像我所展示的那样自己动手)。 Tick库提供了定义概念和约束模板的框架。 Fit库可以处理函数和重载。