【问题标题】:C++17 template deduction guide not used for empty parameter set (ver 2)?C++17 模板推导指南不用于空参数集(版本 2)?
【发布时间】:2017-10-05 01:34:40
【问题描述】:

C++17 template deduction guide not used for empty parameter set? 之后在模板推导指南中遇到了另一个奇怪的问题(不幸的是,那个错误https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81486 在 GCC 主干中仍然没有修复:():

#include <utility>

template <class T> struct success
{
  T value;
  constexpr success(T &&v)
      : value(std::move(v))
  {
  }
  constexpr success(const T &v)
      : value(v)
  {
  }
};
template <> struct success<void>
{
};
template <class T> success(T /*unused*/)->success<T>;
success()->success<void>;

template<class T> struct foo
{
    foo(success<void>) {}
};

int main(void)
{
    auto a = success{5};        // works
    auto b = success{};         // works
    auto c = success{"hello"};  // works
    auto d = success(5);        // works
    //auto e = success();         // FAILS on GCC 7.2!
    auto f = success("hello");  // works
    foo<void> g(success());     // FAILS
    static_assert(std::is_same<decltype(a), success<int>>::value, "");
    static_assert(std::is_same<decltype(b), success<void>>::value, "");
    static_assert(std::is_same<decltype(c), success<const char *>>::value, "");
    static_assert(std::is_same<decltype(d), success<int>>::value, "");
    //static_assert(std::is_same<decltype(e), success<void>>::value, "");
    static_assert(std::is_same<decltype(f), success<const char *>>::value, "");
    return 0;
}

至少对我来说,令人惊讶的是 foo&lt;void&gt; g(success()); 未能在 clang 6.0 主干和 GCC 7 主干上使用模板推导指南,正如您在 https://godbolt.org/g/7m1Zhk 看到的那样

我觉得这令人惊讶,而不是人们所期望的。模板指南说,一个朴素的success() 应构造为success&lt;void&gt;。 foo 的明确构造函数接受success&lt;void&gt; 应该可以正常工作。而是 clang 6.0 中继报告:

34 : <source>:34:17: error: use of class template 'success' requires template arguments; argument deduction not allowed in function return type
    foo<void> g(success());     // FAILS
                ^~~~~~~
3 : <source>:3:27: note: template is declared here
template <class T> struct success
                          ^

以及 GCC 7.3 中继报告:

<source>: In function 'int main()':
34 : <source>:34:25: error: 'auto' parameter not permitted in this context
     foo<void> g(success());     // FAILS
                         ^

谁能解释这里发生了什么?这是 C++ 17 标准中的缺陷吗?

【问题讨论】:

    标签: c++ templates c++17 template-argument-deduction


    【解决方案1】:

    我相信您遇到了最令人烦恼的解析的新形式。

    请记住,任何一段代码的句法形式都是在名称查找之外的任何语义规则应用于它之前确定的。既然 template-name 在语法上是一个有效的 simple-type-specifier,就可以解析

    foo<void> g(success());
    

    作为带有初始化器的对象g 的定义或函数g 的声明。根据 Most Vexing Parse 规则,函数解析“获胜”,因此 g 声明了一个返回 foo&lt;void&gt; 的函数,其一个未命名参数是一个没有返回占位符类模板类型 success 的参数的函数。

    但是当语义检查启动时,这不是类模板占位符类型的有效用途之一,因此程序格式错误。

    注意,如果我们进行一些调整来避免最令人烦恼的解析,clang 将会成功:

    foo<void> g2{success()};
    struct bar { bar(int, succeed<void>) {} };
    bar g3(1, success());
    

    但是,我认为以下双括号技巧也应该有效,但它只会导致来自 clang 的新错误消息。我不确定这个是怎么回事:

    foo<void> g4((success()));
    

    【讨论】:

    • 好的,感谢您确认原因。很难在这个和另一个答案之间做出选择,两者都很好,但是这个也有一个解决方法,所以我认为它更好。
    【解决方案2】:

    这是最麻烦的解析。

    foo<void> g(success());     // FAILS
    

    是一个名为g的函数的声明,它返回一个foo&lt;void&gt;,并作为一个指向返回success的空函数指针类型的[未命名]参数。

    但是,success 不是类型,它是模板名称,并且您不能将模板名称用作函数的返回类型,只能使用完整类型。因此,错误。

    【讨论】:

    • 模板名称,而不是模板 ID。
    • “但是,success 不是类型,它是模板名称,您不能使用模板名称作为函数的返回类型,只能使用完整类型。因此,错误。”我现在看到了问题。未来标准的一种解决方法是,如果模板类型默认其所有模板参数,则可以将该默认值注入到类型空间和模板空间中。是的,我同意这是一个可怕的极端案例,但是令人烦恼的解析问题使我无法用更干净的模板推导指南系统替换一组免费函数和 shim 类型。这是可悲的:(
    猜你喜欢
    • 1970-01-01
    • 2018-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-07
    • 1970-01-01
    相关资源
    最近更新 更多