【问题标题】:Default template parameter & lambda in unevaluated context: bug or feature?未评估上下文中的默认模板参数和 lambda:错误还是功能?
【发布时间】:2019-08-23 19:30:32
【问题描述】:

我们考虑使用完全相同的语法创建两种不同类型的目标。这可以通过 lambdas 轻松完成:

auto x = []{};
auto y = []{};
static_assert(!std::is_same_v<decltype(x), decltype(y)>);

但我们不是使用 lambda,而是寻找另一种更优雅的语法。这里有一些测试。我们首先定义一些工具:

#include <iostream>
#include <type_traits>
#define macro object<decltype([]{})>
#define singleton object<decltype([]{})>

constexpr auto function() noexcept
{
    return []{};
}

template <class T = decltype([]{})>
constexpr auto defaulted(T arg = {}) noexcept
{
    return arg;
}

template <class T = decltype([]{})>
struct object
{
    constexpr object() noexcept {}
};

template <class T>
struct ctad
{
    template <class... Args>
    constexpr ctad(const Args&...) noexcept {}
};

template <class... Args>
ctad(const Args&...) -> ctad<decltype([]{})>;

以及以下变量:

// Lambdas
constexpr auto x0 = []{};
constexpr auto y0 = []{};
constexpr bool ok0 = !std::is_same_v<decltype(x0), decltype(y0)>;

// Function
constexpr auto x1 = function();
constexpr auto y1 = function();
constexpr bool ok1 = !std::is_same_v<decltype(x1), decltype(y1)>;

// Defaulted
constexpr auto x2 = defaulted();
constexpr auto y2 = defaulted();
constexpr bool ok2 = !std::is_same_v<decltype(x2), decltype(y2)>;

// Object
constexpr auto x3 = object();
constexpr auto y3 = object();
constexpr bool ok3 = !std::is_same_v<decltype(x3), decltype(y3)>;

// Ctad
constexpr auto x4 = ctad();
constexpr auto y4 = ctad();
constexpr bool ok4 = !std::is_same_v<decltype(x4), decltype(y4)>;

// Macro
constexpr auto x5 = macro();
constexpr auto y5 = macro();
constexpr bool ok5 = !std::is_same_v<decltype(x5), decltype(y5)>;

// Singleton
constexpr singleton x6;
constexpr singleton y6;
constexpr bool ok6 = !std::is_same_v<decltype(x6), decltype(y6)>;

以及以下测试:

int main(int argc, char* argv[])
{
    // Assertions
    static_assert(ok0); // lambdas
    //static_assert(ok1); // function
    static_assert(ok2); // defaulted function
    static_assert(ok3); // defaulted class
    //static_assert(ok4); // CTAD
    static_assert(ok5); // macro
    static_assert(ok6); // singleton (macro also)

    // Display
    std::cout << ok1 << std::endl;
    std::cout << ok2 << std::endl;
    std::cout << ok3 << std::endl;
    std::cout << ok4 << std::endl;
    std::cout << ok5 << std::endl;
    std::cout << ok6 << std::endl;

    // Return
    return 0;
}

这是使用当前主干版本的 GCC 编译的,带有选项 -std=c++2a。在编译器资源管理器中查看结果here


ok0ok5ok6 工作的事实并不令人意外。然而,ok2ok3trueok4 对我来说并不令人惊讶。

  • 有人可以解释使ok3 trueok4 false 的规则吗?
  • 它真的应该如何工作,或者这是一个与实验性功能(未评估上下文中的 lambdas)有关的编译器错误? (非常欢迎参考标准或 C++ 提案)

注意:我真的希望这是一个功能而不是错误,只是因为它可以实现一些疯狂的想法

【问题讨论】:

标签: c++ lambda language-lawyer template-meta-programming c++20


【解决方案1】:

有人可以解释使 ok3 为真的规则吗 但是 ok4 错误?

ok3 为真,因为使用 lambdas 类型作为默认类型。

lambda 表达式的类型(也是闭包的类型 object) is a unique,未命名的非联合类类型,

因此,object 的默认模板类型、macrosingltone 的模板参数类型在每次实例化后总是不同的。但是,对于函数function,调用返回的 lambda 是唯一的,并且它的类型是唯一的。模板函数ctad 具有仅用于参数的模板,但返回值是唯一的。如果重写函数为:

template <class... Args, class T =  decltype([]{})>
ctad(const Args&...) -> ctad<T>;

在这种情况下,每次实例化后返回类型都会不同。

【讨论】:

    【解决方案2】:

    对于 ok2 函数参数类型 (T) 取决于指定的模板参数。 因为 ok3 ctor 不是模板。

    对于 ok4,两个扣除都依赖于相同的参数类型列表(在这种情况下为空),因此扣除只发生一次。模板实例化和推导是不同的东西。而对于相同的参数类型列表推导只发生一次,实例化发生在所有用法。

    查看此代码 (https://godbolt.org/z/ph1Wk2)。如果扣除的参数不同,则会发生单独的扣除。

    【讨论】:

    • 由于问题被标记为language-lawyer,您能否提供一些标准参考。也许引用“推论只发生一次”
    • 我没有看标准,但我们知道模板实例化可以发生在模板定义之外。但推论必须具备相关功能,没有必要多次发生。我认为它没有在标准中指定。同样在此示例中,Args 不依赖于 T。对于给定的函数,“ctad(const Args&...)”变为“ctad()”,因此 Args 是一个空参数包。
    猜你喜欢
    • 1970-01-01
    • 2019-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-09
    • 1970-01-01
    • 2020-02-04
    • 1970-01-01
    相关资源
    最近更新 更多