【问题标题】:Compiler variance in function template argument deduction函数模板参数推导中的编译器差异
【发布时间】:2021-12-30 05:43:30
【问题描述】:

以下程序:

#include <type_traits>

template<typename T, bool b>
struct S{
    S() = default;
    
    template<bool sfinae = true,
             typename = std::enable_if_t<sfinae && !std::is_const<T>::value>>
    operator S<T const, b>() { return S<T const, b>{}; }
};

template<typename T, bool b1, bool b2>
void f(S<const std::type_identity_t<T>, b1>,
                                 // ^- T in non-deduced context for func-param #1 
       S<T, b2>)
      // ^- T deduced from here
{}                         

int main() {
    S<int, true> s1{};
    S<int, false> s2{};
    f(s1, s2);
}

被 GCC (11.2) 接受,但被 Clang (13) 和 MSVC (19.latest) 拒绝,全部用于 -std=c++20 / /std:c++20 (DEMO)。

  • 这里什么编译器是正确的?

【问题讨论】:

  • 我正在添加一个我认为可能是准确的自我回答,这是我在写问题时发现的,但我希望(至少!)第二意见。
  • bool x 确实可以推导出来,但是x 不能从S&lt;const int, x&gt; 推导出S&lt;int, true&gt;

标签: c++ templates compiler-errors language-lawyer template-argument-deduction


【解决方案1】:

这由[temp.deduct.call] 管理,尤其是/4

一般来说,推导过程会尝试找到模板参数值,使推导的AA 相同(在A 类型按上述转换后)。但是,有三种情况允许有所不同:[...]

在 OP 的示例中,AS&lt;const int, true&gt; 并且(转换后的)AS&lt;int, int&gt;,并且这 [三种] 情况都不适用于这里,意味着推论失败。

我们可以通过调整程序来对此进行试验,以使推导的A 与转换后的A 差异属于三种情况之一;说[temp.deduct.call]/4.3

  • 如果P 是一个类并且P 具有simple-template-id 形式,则转换后的A 可以是推导A 的派生类D。 [...]
#include <type_traits>

template<typename T, bool b>
struct S : S<const T, b> {};

template<typename T, bool b>
struct S<const T, b> {};

template<typename T, bool b1, bool b2>
void f(S<const std::type_identity_t<T>, b1>, S<T, b2>){}                         

int main() {
    S<int, true> s1{};
    S<int, true> s2{};
    f(s1, s2);
}

这个程序被所有三个编译器正确接受 (DEMO)。

因此,GCC 很可能在此处存在错误,因为上述错误是可诊断的(不是格式错误的 NDR)。由于我找不到这些问题的未解决错误,因此我提交了:

  • Bug 103333 - [accepts-invalid] 不兼容的“转换 A”/“推断 A”对的函数模板参数推导

我们可能还注意到[temp.arg.explicit]/7 包含一个特殊情况,允许隐式转换将参数类型转换为函数参数类型:

将在函数参数上执行隐式转换([conv])以将其转换为相应函数参数的类型如果参数类型不包含参与模板参数推导的模板参数 .

适用于 OP 的示例,因为(函数)参数类型 S&lt;const std::type_identity_t&lt;T&gt;, b1&gt; 还包含(非类型)模板参数 b1,它参与模板参数推导.

但是在以下程序中:

#include <type_traits>

template<typename T>
struct S{
    S() = default;
    
    template<bool sfinae = true,
             typename = std::enable_if_t<sfinae && !std::is_const<T>::value>>
    operator S<T const>() { return S<T const>{}; }
};

template<typename T>
void f(S<const std::type_identity_t<T>>, S<T>) {}                         

int main() {
    S<int> s1{};
    S<int> s2{};
    f(s1, s2);
}

(函数)参数类型是A&lt;std::type_identity_t&lt;T&gt;&gt;,其中唯一的模板参数是T,它不参与该参数-参数对的模板参数推导(P/A)。因此,[temp.arg.explicit]/7 确实适用于此,并且程序格式正确。

【讨论】:

    猜你喜欢
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 2018-12-05
    • 2021-08-31
    • 2020-01-21
    相关资源
    最近更新 更多