CRTP、模板、元编程、转发和静态成员:g++ 4.8 中的一个错误

CRTP, templates, metaprogramming, forwarding and static member: a bug in g++ 4.8?

本文关键字:错误 一个 g++ 编程 模板 转发 CRTP 静态成员      更新时间:2023-10-16

首先,对于这段代码的质量差,我真的很抱歉,但我已经花了 1 个小时来隔离问题的根源,我没有比这更短的例子。所以这是代码:

#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
#include <array>
template <class Crtp, class... Types>
struct Base
{
    template <
        unsigned int Index,
        class Type = typename std::tuple_element<Index, std::tuple<Types...> >::type
    >
    inline const Type& get() const {
        return std::get<Index>(data);
    }
    template <
        unsigned int Index,
        class Type = typename std::tuple_element<Index, std::tuple<Types...> >::type
    >
    inline Crtp& set(const Type& value) {
        std::get<Index>(data) = value; return static_cast<Crtp&>(*this);
    }
    std::tuple<Types...> data;
};
template <typename Type, unsigned int Size>
struct Derived : public Base<Derived<Type, Size>, std::array<Type, Size>>
{
    template <
        class... Args,
        class Template = decltype(std::declval<const Base<
            Derived<Type, Size>,
            std::array<Type, Size>
        >>().template get<0>(std::declval<Args>()...))
    >
    inline Template test(Args&&... args) const {
         return this->template get<0>(std::forward<Args>(args)...);
    } 
    template <
        class... Args,
        class Template = decltype(std::declval<const Base<
           Derived<Type, Size>, 
           std::array<Type, Size>
        >>().template set<0>(std::declval<Args>()...))
    >
    inline Derived<Type, Size>& test(Args&&... args) {
        return this->template set<0>(std::forward<Args>(args)...);
    } 
    static void check() {
         Derived<double, 3> derived;
         std::cout<<derived.test()[0]<<std::endl;
    }
};
int main(int argc, char* argv[])
{
    Derived<double, 3> derived;
    std::cout<<derived.test()[0]<<std::endl; // Working
    Derived<double, 3>::check(); // Not working: error: no match for ‘operator[]’ (operand types are ‘Derived<double, 3u>’ and ‘int’)
    return 0;
}

说明所做的事情:有一个 Base 类将派生类 (CRTP( 和元组类型作为模板参数。这个基类有两个成员:一个用于get tuple的第n个元素,另一个用于set tuple的第n个元素。然后,有一个从Base类继承的Derived类,并将std::array放在基类的元组中:因此,该派生类的数据类型为:std::tuple<std::array<Type, Size>> data。这个派生类有一个重载函数test()该函数根据其参数调用getset函数:test()将调用get()test(std::array<double, 3>{1, 2, 3})将调用set(std::array<double, 3>{1, 2, 3})。因此test()[0]应该返回数组的第一个元素:它在main()中工作,但在静态函数中不起作用。

我不知道编译器试图做什么,但显然这是行不通的。我认为这是g++ 4.8.1中的一个错误(我没有尝试过其他版本(,但我想确定这一点。

所以这是我的问题:

  • 你能确认这个错误(也许找到一个解释(吗?
  • 您是否有一个更短且不太复杂的示例来说明问题?
">

编译器试图做什么"在调用时错误地在Derived::test的常量和非常量重载之间执行重载解析check()。您可以通过插入看到

std::cout << typeid(decltype(derived.test())).name() << std::endl;

check()("7DerivedIdLj3EE"(和main()("St5arrayIdLy3EE"(中。

编辑:一些调查,交替注释掉常量/非常量测试重载,表明模板推导对于check()test的非恒定重载并没有失败。参数包Args为空,因此替换失败应发生在确定不带参数的set<0>的返回类型的decltype表达式中。

当你在思考为什么时,我建议你简化你的代码以避免它:

#include <iostream>
#include <utility>
#include <tuple>
#include <array>
template <class Crtp, class... Types>
struct Base
{
    std::tuple<Types...> data;
    template <unsigned int Index>
    auto get() const -> decltype(std::get<Index>(data)) {
        return std::get<Index>(data);
    }
    template <unsigned int Index, typename T>
    Crtp& set(T&& value) {
        std::get<Index>(data) = std::forward<T>(value);
        return static_cast<Crtp&>(*this);
    }
};
template <typename Type, unsigned int Size>
struct Derived : public Base<Derived<Type, Size>, std::array<Type, Size>>
{
    auto test() const -> decltype(this->template get<0>()) {
        return this->template get<0>();
    }
    template <typename T>
    Derived& test(T&& value) {
        return this->template set<0>(std::forward<T>(value));
    }
    static void check() {
        Derived<double, 3> derived;
        std::cout << derived.test()[0] << std::endl;
    }
};
int main(int, char*[])
{
    Derived<double, 3> derived;
    std::cout << derived.test()[0] << std::endl;
    Derived<double, 3>::check();
}
相关文章: