转发 CRTP 派生类不兼容 MSCV/GCC 中定义的返回类型

Forwarding of return type defined in CRTP derived class incompatibility MSCV/GCC

提问人:Johny Siemano Kolano 提问时间:11/18/2023 更新时间:11/18/2023 访问量:55

问:

我遇到了 CRTP(奇怪的重复模板模式)基类中的模板函数问题,该基类在 MSVC 和 GCC 编译器上表现出不同的行为。BaseA 基类包含一个模板成员函数 foo(),该函数应该返回继承类中定义的类型。但是,foo() 的两种不同实现在 MSVC 和 GCC 上产生不同的结果。我见过几个类似的stackoverflow线程,但它们都没有解决我的问题。

检查下面的代码或使用此 goldbold 链接

// Minimal working example

template <typename T>
class BaseA {
    public:
        //// Version 1 (not working on MSVC and GCC)
        //// it is incomplete type usage so it obviously wont work
        //T::value_type foo() const {
        //    return static_cast<const T*>(this)->foo();
        //}

        //// Version 2 (working on MSVC, not working on GCC)
        //// this in my opinion should work everywhere
        //// since type U will be deduced after T from BaseA
        //// but unluckly it does only work on MSVC
        //// GCC error: invalid use of incomplete type 'class DerivedA'
        template <typename U = typename T::value_type>
        U foo() const {
            return static_cast<const T*>(this)->foo();
        }

        //// Version 3 (working on MSVC and GCC)
        //// will work, but "auto" as interface function return value is unaccepted solution.
        //// This is only minimal working example, real program is much bigger and it can lead to
        //// many issues in the future.
        //auto foo() const {
        //    return static_cast<const T*>(this)->foo();
        //}
};

class DerivedA : BaseA<DerivedA> {
    public:
        using value_type = int;

        value_type foo() const {
            return 5;
        }
};

template <typename T>
void some_interface_call(const BaseA<T>& a) {
    auto value = a.foo();
}

int main() {
    BaseA<DerivedA> a;
    some_interface_call(a);

    return 0;
}

传达这种类型的正确、通用方法是什么? 有没有办法在 GCC 下使用尽可能少的更改?Version 2

C 模板 GCC Visual-C++ C++ 20

评论

0赞 Jarod42 11/18/2023
T在 CRTP 中不完整,因此不能在声明中使用。T::value_type
0赞 Jarod42 11/18/2023
外部性状怎么样,.template <typename T> struct my_value_type; template <> my_value_type<MyObj> { using type = SomeType; /* but not T::type */ };
0赞 Johny Siemano Kolano 11/18/2023
@Jarod42确定我可以,只需要在类中用另一个模板包装它,签入呈现的代码Version 2

答:

3赞 Artyer 11/18/2023 #1

这不起作用,因为在声明成员函数模板时,模板的默认值必须有效。

我所看到的“标准解决方法”是在声明中有一个和使用:typename U = TUT

template <typename U = T>
typename U::value_type foo() const {
    return static_cast<const T*>(this)->foo();
}

评论

0赞 Johny Siemano Kolano 11/18/2023
我检查了它,它可以在 Linux (GCC) 和 Windows (MSVC) 下工作,我一直使用它 - 但这些天我主要在 Windows 上编程,它一直在那里工作。从来没有这样写过。谢谢!Version 2template <typename U = T::value_type>