Mq-b/Modern-Cpp-templates-tutorial

01函数模板中的代码,gcc13.2 及以前的编译器运行存在 bug,已在 gcc14.1 被修复

Closed this issue · 8 comments

https://github.com/Mq-b/Modern-Cpp-templates-tutorial/blob/main/md/%E7%AC%AC%E4%B8%80%E9%83%A8%E5%88%86-%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/01%E5%87%BD%E6%95%B0%E6%A8%A1%E6%9D%BF.md

template<typename T1,typename T2,typename RT = 
    decltype(true ? T1{} : T2{}) >
RT max(const T1& a, const T2& b) { // RT 是 std::string
    return a > b ? a : b;
}

在gcc不能运行
https://godbolt.org/z/685Y95vKW

T1,T2不一定是可{}构造的,应用declval,且去除引用返回
需改成如下

template<typename T1,typename T2,typename RT = 
    std::remove_reference_t< decltype(true ? 
    std::declval<T1>() : std::declval<T2>())> >
RT max(const T1& a, const T2& b) { // RT 是 std::string
    return a > b ? a : b;
}

https://godbolt.org/z/xd14YaPx6

Mq-b commented

T1,T2不一定是可{}构造的,应用declval,且去除引用返回

我完全清楚的知道这个问题,这是我故意为之,我并不想在此时就开始教学 std::declval,过于抽象,前期视频使用简单的 T1{} 这样的写法,带入想象 int{} 更易教学。

你可以关注到教案的后续内容,在 SFINAE 中单独拿出一节来讲这个问题:

错误的原因很简单,decltype(T{} + T{}) 这个表达式中,同时要求了 T 类型支持默认构造(虽然这不是我们的本意),然而我们的 X2 类型没有默认构造,自然而然 T{} 不是合法表达式,代换失败。其实我们之前也有类似的写法,我们在本节进行纠正,使用 std::declval

Mq-b commented

在gcc不能运行

GCC 的报错是:

error: taking address of temporary array

先留着吧,把函数调用那里换成 ::max 还实在一点,,,

我感觉这是 GCC 的 bug,至少数组和 std::string 完全可以直接({})构造,这里涉及到了一些其他的问题可能。

Mq-b commented
#include <iostream>
#include <type_traits>

using namespace std::string_literals;

template<typename T1, typename T2, 
    typename RT = decltype(true ? T1{} : T2{})> 
RT max (T1 a, T2 b) {
    return b < a ? a : b;
}

int main(){
    auto ret = ::max("1", "2"s);
    std::cout << ret << '\n';
}

gcc 可以通过编译,我只是去除了 CVR,我感觉没有道理:https://godbolt.org/z/sWWePaf99

这个 labels 怎么乱七八糟的。

是 gcc 的 bug ,已经修复了。

参考:

看起来大家都没有习惯检查 trunk 的表现……

Mq-b commented

合理,和我猜想一样。

的确没有检查 trunk 的习惯。

ce4ce40

Mq-b commented

已发布 gcc14,解决了 gcc13.2 及以前的这块 bug,再次强调一下。
https://godbolt.org/z/MWKv8roo8