的用途 build 。在他的演-6ren">
gpt4 book ai didi

c++ - void_t "can implement concepts"?

转载 作者:IT老高 更新时间:2023-10-28 12:11:48 25 4
gpt4 key购买 nike

我在看Walter Brown's CppCon2014 talk on template metaprogramming的第二部分,期间他讨论了他的小说void_t<>的用途 build 。在他的演讲中,Peter Sommerlad 问了他一个我不太明白的问题。 (链接直接指向问题,讨论中的代码直接发生在此之前)

萨默拉德问道

Walter, would that mean we actually can implement concepts lite right now?



沃尔特对此作出回应

Oh yeah! I've done it ... It doesn't have quite the same syntax.



我理解这次交流是关于 Concepts Lite。这种模式真的有那么万能吗?无论出于何种原因,我都没有看到它。有人可以解释(或草图)这样的东西会是什么样子吗?这只是关于 enable_if和定义特征,或者提问者指的是什么?
void_t模板定义如下:
template<class ...> using void_t = void;

然后他使用它来检测类型语句是否格式正确,并使用它来实现 is_copy_assignable类型特征:
//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&> {};

由于谈话,我明白这个例子是如何工作的,但我不明白我们如何从这里得到像 Concepts Lite 这样的东西。

最佳答案

是的,概念精简版基本上装扮了 SFINAE。此外,它还允许进行更深入的内省(introspection),以实现更好的重载。但是,这仅在概念谓词定义为 concept bool 时才有效。 .改进的重载不适用于当前的概念谓词,但可以使用条件重载。让我们看看如何在 C++14 中定义谓词、约束模板和重载函数。这有点长,但它介绍了如何在 C++14 中创建完成此任务所需的所有工具。

定义谓词

首先,阅读带有所有 std::declval 的谓词有点难看。和 decltype到处。相反,我们可以利用这样一个事实,即我们可以使用尾随 decltype(来自 Eric Niebler 的博客文章 here)来约束函数,如下所示:

struct Incrementable
{
template<class T>
auto requires_(T&& x) -> decltype(++x);
};

所以如果 ++x无效,则 requires_成员函数不可调用。所以我们可以创建一个 models仅检查是否 requires_ 的特征可以使用 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 ,但我们可以使用这个宏来帮助使它更干净:
#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0

所以我们可以定义一个 increment基于 Incrementable 约束的函数概念:
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)>())>
^

重载函数

现在如果我们要进行重载,我们要使用条件重载。假设我们要创建一个 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;
}

然而,当它与 concept bool 一起使用时,这会导致模棱两可的重载(在概念精简版中,这仍然是模棱两可的重载,除非我们将谓词更改为引用 std::vector 中的其他谓词)。迭代器。我们想要做的是对调用进行排序,我们可以使用条件重载来完成。可以考虑写这样的东西(这不是有效的 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;
}

所以如果第一个函数没有被调用,它将调用下一个函数。因此,让我们从为两个功能实现它开始。我们将创建一个名为 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适用于使用递归的任意数量的函数:
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 = {};

使用 Lambda 重载

然而,此外,我们可以使用 lambda 来代替函数对象来编写它,这有助于使其编写起来更清晰。所以我们使用这个 STATIC_LAMBDA 在编译时构造 lambda 的宏:
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 {};
}

然后我们现在可以写 advance像这样的功能:
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功能降低 decltype丑陋:
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;
}
);

最后,如果您有兴趣使用现有的库解决方案(而不是像我展示的那样滚动自己的解决方案)。有 Tick为定义概念和约束模板提供框架的库。和 Fit库可以处理函数和重载。

关于c++ - void_t "can implement concepts"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26513095/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com