C++ 无效“t”;可以实施概念吗;?

C++ 无效“t”;可以实施概念吗;?,c++,templates,c++11,template-meta-programming,c++-concepts,C++,Templates,C++11,Template Meta Programming,C++ Concepts,我在看《圣经》的第二部分,其间他讨论了他的小说《空》的用法。在他的演讲中,彼得·索默拉德问了他一个我不太明白的问题。(链接直接指向问题,正在讨论的代码就发生在这之前) 萨默拉德问道 Walter,这是否意味着我们现在就可以实现lite概念 沃尔特对此作出了回应 哦,是的!我已经做到了。。。它没有完全相同的语法 我理解这次交流是关于概念的。这种款式真的那么通用吗?不管什么原因,我都看不到。有人能解释(或描绘)像这样的东西看起来是什么样子吗?这仅仅是关于enable\u if和定义特征,还是提问者指

我在看《圣经》的第二部分,其间他讨论了他的小说《空》的用法。在他的演讲中,彼得·索默拉德问了他一个我不太明白的问题。(链接直接指向问题,正在讨论的代码就发生在这之前)

萨默拉德问道

Walter,这是否意味着我们现在就可以实现lite概念

沃尔特对此作出了回应

哦,是的!我已经做到了。。。它没有完全相同的语法

我理解这次交流是关于概念的。这种款式真的那么通用吗?不管什么原因,我都看不到。有人能解释(或描绘)像这样的东西看起来是什么样子吗?这仅仅是关于
enable\u if
和定义特征,还是提问者指的是什么

void\t
模板定义如下:

template<class ...> using void_t = void;

通过这次演讲,我理解了这个例子是如何工作的,但我不明白我们是如何从这里发展到类似概念精简版的东西。

是的,概念精简版基本上是在美化SFINAE。此外,它允许更深入的内省,以允许更好的过载。但是,只有将概念谓词定义为
concept bool
时,这才有效。改进的重载不适用于当前的概念谓词,但可以使用条件重载。让我们看看如何在C++14中定义谓词、约束模板和重载函数。这有点长,但它讨论了如何在C++14中创建实现这一点所需的所有工具

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

struct Incrementable
{
    template<class T>
    auto requires_(T&& x) -> decltype(++x);
};
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)>())>
                  ^
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 = {};
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;
    }
);
约束模板 因此,当我们想要基于该概念约束模板时,我们仍然需要使用
enable_if
,但我们可以使用此宏帮助使其更干净:

#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0
因此,如果我们使用不可增量的东西调用
增量
,我们将得到如下错误:

struct Incrementable
{
    template<class T>
    auto requires_(T&& x) -> decltype(++x);
};
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)>())>
                  ^
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 = {};
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;
    }
);
但是,当它与
std::vector
迭代器一起使用时,这会导致不明确的重载(在概念lite中,这仍然是不明确的重载,除非我们将谓词更改为引用
concept bool
中的其他谓词)。我们要做的是对调用进行排序,这可以使用条件重载来完成。可以这样写(这不是有效的C++):

因此,这意味着我们需要将函数定义为函数对象:

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 = {};
它将编译并打印出
5

但是,
std::advance
实际上有三个重载,因此我们可以使用
basic\u conditional
来实现
conditional
,它适用于使用递归的任意数量的函数:

template<class F, class... Fs>
struct conditional : basic_conditional<F, conditional<Fs...>>
{};

template<class F>
struct conditional<F> : F
{};
使用Lambdas重载 然而,另外,我们可以使用lambdas来编写它,而不是使用函数对象,这有助于使编写更简洁。因此,我们使用此宏在编译时构造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() + []
然后我们现在可以像这样编写
advance
函数:

struct Incrementable
{
    template<class T>
    auto requires_(T&& x) -> decltype(++x);
};
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)>())>
                  ^
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 = {};
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;
    }
);

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

有趣的问题,不幸的是,它与“请看一看我的源代码,托管在(某个存储库)上”没有什么不同。更糟糕的是,这是一个视频。您是否可以在问题中添加相关的代码片段,使其更加独立?至少,在你谈论的视频中指定时间,“第二部分”几乎没有用。@BenVoigt好的,我更新了问题。youtube链接中有一个直接跳到问题的时间代码。我添加了他在演讲中使用的主要示例。我希望这能让问题变得更清楚。你不能从那里开始。这是一种很酷的技术,您可以将其用于模板参数列表中的nice“requires”子句,但您无法让编译器在模板上施加偏序,而“concept”是基于该偏序更精确的。您也不能对类模板的非模板成员函数或概念TS中的其他一些全新的语言功能强加要求。@JonathanWakely好的,这些是您提到的“requires”子句吗(我们的伪子句)如果具有更易访问、更容易编写的约束和类型特征,那么就打扮一下
enable_,还是更复杂的东西?是的,我相信这只是一种非常酷的SFINAE方法。Concepts Lite比SFINAE多得多,它完全取代了SFINAE,而不是用漂亮的语法来修饰它。感谢您花时间写出如此详尽的答案。这比我预期的要多,但正是我所希望的答案。由于高质量和高数量。。。但是OMG我的C++ 11恶心是踢在FWIW,用于实现静态-LAMBDA的技巧不是标准兼容的C++。它在实践中是有效的,因为如果一个系统不工作,它必须非常奇怪,但这并不能使它100%洁净。@mattnewport是的,它匹配一个函数类型,
Ts…
匹配函数参数,
Concept
匹配函数类型的返回类型。啊,好的,我想我现在明白了。让我感到困惑的是,没有看到一个具有该签名的函数来自何处,但实际上从来没有这样一个函数,它只是一种方便的语法,用于模式匹配类型。聪明。
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;
    }
);