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

时间:2013-07-07 18:37:07

标签: c++ templates c++11 g++

首先,我真的很抱歉这段代码的质量很差,但我已经用了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的第tuplesettuple的第n个元素的DerivedBase。 然后,有一个std::array类继承自std::tuple<std::array<Type, Size>> data类,并将test()放在基类的元组中:因此,此派生类的数据类型为:{ {1}}。此派生类具有重载函数get,根据其参数调用settest()函数:get()将调用test(std::array<double, 3>{1, 2, 3})set(std::array<double, 3>{1, 2, 3})将致电test()[0]。因此main()应该返回数组的第一个元素:它在g++ 4.8.1中工作,但它在静态函数中不起作用。

我不知道编译器正在尝试做什么,但显然这不起作用。 我认为这是{{1}}中的一个错误(我没有尝试过其他版本),但我想确定这一点。

所以这是我的问题:

  • 您能否确认此错误(也许可以找到解释)?
  • 您是否有一个较短且不太复杂的例子来说明问题?

1 个答案:

答案 0 :(得分:1)

“编译器尝试做什么”在Derived::test内调用时没有参数的check()的常量和非常量重载之间错误地执行重载解析。你可以通过插入来看到

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

check()(“7DerivedIdLj3EE”)和main()(“St5arrayIdLy3EE”)。

编辑:一些调查,交替评论const / nonconst测试重载,表明模板推导不会失败testcheck()的非常数重载。参数包Args为空,因此替换失败应发生在decltype表达式中,该表达式确定没有参数的set<0>的返回类型。

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

#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();
}